<template>
  <div class="webrtc-stream-view">
    <div class="bg-white rounded-2xl shadow-lg p-6 border border-gray-100">
      <div class="flex items-center justify-between mb-4">
        <h3 class="text-lg font-semibold text-gray-800 flex items-center">
          <i class="fa-solid fa-broadcast-tower mr-2 text-purple-500"></i>
          WebRTC流媒体SLAM
        </h3>
        <div class="flex items-center space-x-2">
          <span :class="getStatusColor(connectionStatus)" class="px-2 py-1 rounded-full text-xs font-medium">
            {{ getStatusText(connectionStatus) }}
          </span>
          <button 
            @click="toggleFullscreen" 
            class="px-3 py-1 bg-gray-500 text-white rounded text-xs hover:bg-gray-600 transition-colors"
          >
            <i class="fa-solid fa-expand mr-1"></i>
            全屏
          </button>
        </div>
      </div>
      
      <!-- 双视频显示区域 -->
      <div class="relative bg-gray-900 rounded-lg overflow-hidden mb-4" :class="fullscreen ? 'fixed inset-0 z-50' : 'h-96'">
        <!-- 主视频流 -->
        <div class="absolute inset-0">
          <video 
            ref="mainVideo" 
            class="w-full h-full object-cover" 
            autoplay 
            muted 
            playsinline
            @loadedmetadata="handleMainVideoLoaded"
            @error="handleMainVideoError"
          ></video>
          
          <!-- 主视频信息覆盖层 -->
          <div class="absolute top-4 left-4 bg-black bg-opacity-60 text-white text-xs p-3 rounded-md space-y-1">
            <p>主视频流</p>
            <p>分辨率: {{ mainVideoInfo.width }}x{{ mainVideoInfo.height }}</p>
            <p>帧率: {{ mainVideoInfo.fps }}fps</p>
            <p>状态: <span :class="getVideoStatusColor(mainVideoStatus)">{{ getVideoStatusText(mainVideoStatus) }}</span></p>
          </div>
        </div>
        
        <!-- SLAM处理视频流 -->
        <div class="absolute top-4 right-4 w-80 h-60 bg-black bg-opacity-80 rounded-lg overflow-hidden">
          <video 
            ref="slamVideo" 
            class="w-full h-full object-cover" 
            autoplay 
            muted 
            playsinline
            @loadedmetadata="handleSlamVideoLoaded"
            @error="handleSlamVideoError"
          ></video>
          
          <!-- 关键点绿色框覆盖层 -->
          <div class="absolute inset-0 pointer-events-none">
            <canvas 
              ref="keypointCanvas" 
              class="absolute inset-0 w-full h-full"
              :style="{ zIndex: 10 }"
            ></canvas>
          </div>
          
          <!-- SLAM信息覆盖层 -->
          <div class="absolute bottom-0 left-0 right-0 bg-black bg-opacity-70 text-white text-xs p-2">
            <div class="flex justify-between items-center">
              <span>SLAM处理</span>
              <span :class="getVideoStatusColor(slamVideoStatus)">{{ getVideoStatusText(slamVideoStatus) }}</span>
            </div>
            <div class="flex justify-between text-xs mt-1">
              <span>关键帧: {{ slamInfo.keyframes }}</span>
              <span>地图点: {{ slamInfo.mapPoints }}</span>
            </div>
          </div>
        </div>
        
        <!-- 加载状态 -->
        <div v-if="loading" class="absolute inset-0 flex items-center justify-center bg-black/50">
          <div class="text-center text-white">
            <i class="fa-solid fa-spinner fa-spin text-2xl mb-2"></i>
            <p>正在连接WebRTC流...</p>
          </div>
        </div>
        
        <!-- 连接失败状态 -->
        <div v-if="connectionStatus === 'ERROR'" class="absolute inset-0 flex items-center justify-center bg-black/50">
          <div class="text-center text-white">
            <i class="fa-solid fa-exclamation-triangle text-4xl mb-4 text-red-400"></i>
            <p class="text-lg mb-2">WebRTC连接失败</p>
            <p class="text-sm text-gray-400 mb-4">{{ errorMessage }}</p>
            <button @click="reconnect" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
              <i class="fa-solid fa-refresh mr-1"></i>
              重新连接
            </button>
          </div>
        </div>
        
        <!-- 控制面板 -->
        <div class="absolute bottom-4 left-4 bg-black/70 rounded-lg p-3 text-white">
          <div class="flex items-center space-x-4">
            <!-- 会话选择 -->
            <div class="flex items-center space-x-2">
              <label class="text-xs text-gray-300">会话:</label>
              <select 
                v-model="selectedSessionId" 
                class="px-2 py-1 bg-gray-700 text-white rounded text-xs border border-gray-600"
                @change="onSessionChange"
              >
                <option value="">请选择会话</option>
                <option v-for="session in availableSessions" :key="session.id" :value="session.id">
                  {{ session.sessionName }}
                </option>
              </select>
            </div>
            
            <button 
              @click="toggleMainVideo" 
              :class="mainVideoPlaying ? 'bg-red-500' : 'bg-green-500'"
              class="px-3 py-1 rounded text-xs"
            >
              <i :class="mainVideoPlaying ? 'fa-pause' : 'fa-play'" class="fa-solid mr-1"></i>
              {{ mainVideoPlaying ? '暂停' : '播放' }}
            </button>
            
            <button 
              @click="toggleSlamVideo" 
              :class="slamVideoPlaying ? 'bg-red-500' : 'bg-green-500'"
              class="px-3 py-1 rounded text-xs"
            >
              <i :class="slamVideoPlaying ? 'fa-pause' : 'fa-play'" class="fa-solid mr-1"></i>
              SLAM {{ slamVideoPlaying ? '暂停' : '播放' }}
            </button>
            
            <button 
              @click="startSlam" 
              :disabled="!mainVideoPlaying || !selectedSessionId || slamProcessing"
              class="px-3 py-1 bg-purple-500 text-white rounded text-xs disabled:opacity-50"
            >
              <i :class="slamProcessing ? 'fa-check' : 'fa-play'" class="fa-solid mr-1"></i>
              {{ slamProcessing ? 'SLAM运行中' : '开始SLAM' }}
            </button>
            
            <button 
              @click="stopSlam" 
              :disabled="!slamProcessing"
              class="px-3 py-1 bg-red-500 text-white rounded text-xs disabled:opacity-50"
            >
              <i class="fa-solid fa-stop mr-1"></i>
              停止SLAM
            </button>
            
            <button 
              @click="reconnect" 
              class="px-3 py-1 bg-blue-500 text-white rounded text-xs hover:bg-blue-600"
            >
              <i class="fa-solid fa-refresh mr-1"></i>
              重连
            </button>
            
          </div>
        </div>
        
        <!-- 退出全屏按钮 -->
        <button 
          v-if="fullscreen" 
          @click="exitFullscreen"
          class="absolute bottom-4 right-4 p-2 bg-black/70 text-white rounded-full hover:bg-black/80 transition-colors"
        >
          <i class="fa-solid fa-times"></i>
        </button>
      </div>
      
      <!-- SLAM统计信息 -->
      <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
        <div class="bg-gradient-to-r from-red-50 to-red-100 rounded-lg p-4">
          <div class="flex items-center justify-between">
            <div>
              <p class="text-sm text-red-600">关键帧</p>
              <p class="text-2xl font-bold text-red-800">{{ slamInfo.keyframes }}</p>
            </div>
            <i class="fa-solid fa-key text-red-500 text-2xl"></i>
          </div>
        </div>
        
        <div class="bg-gradient-to-r from-blue-50 to-blue-100 rounded-lg p-4">
          <div class="flex items-center justify-between">
            <div>
              <p class="text-sm text-blue-600">地图点</p>
              <p class="text-2xl font-bold text-blue-800">{{ slamInfo.mapPoints }}</p>
            </div>
            <i class="fa-solid fa-circle text-blue-500 text-2xl"></i>
          </div>
        </div>
        
        <div class="bg-gradient-to-r from-green-50 to-green-100 rounded-lg p-4">
          <div class="flex items-center justify-between">
            <div>
              <p class="text-sm text-green-600">轨迹长度</p>
              <p class="text-2xl font-bold text-green-800">{{ slamInfo.trajectoryLength.toFixed(2) }}m</p>
            </div>
            <i class="fa-solid fa-route text-green-500 text-2xl"></i>
          </div>
        </div>
        
        <div class="bg-gradient-to-r from-purple-50 to-purple-100 rounded-lg p-4">
          <div class="flex items-center justify-between">
            <div>
              <p class="text-sm text-purple-600">处理进度</p>
              <p class="text-2xl font-bold text-purple-800">{{ slamInfo.progress.toFixed(1) }}%</p>
            </div>
            <i class="fa-solid fa-chart-line text-purple-500 text-2xl"></i>
          </div>
        </div>
      </div>
      
      <!-- 增强版SLAM 3D轨迹信息 -->
      <div v-if="slamInfo.has3DCoordinates" class="bg-gradient-to-r from-indigo-50 to-indigo-100 rounded-lg p-4 mb-4">
        <div class="flex items-center justify-between mb-3">
          <h4 class="text-lg font-semibold text-indigo-800 flex items-center">
            <i class="fa-solid fa-cube mr-2"></i>
            3D轨迹数据
          </h4>
          <span class="px-2 py-1 bg-indigo-200 text-indigo-800 rounded-full text-xs font-medium">
            {{ slamInfo.algorithm || 'Enhanced SLAM' }}
          </span>
        </div>
        
        <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
          <div class="bg-white rounded-lg p-3">
            <p class="text-sm text-indigo-600">轨迹点数</p>
            <p class="text-xl font-bold text-indigo-800">{{ slamInfo.trajectoryPointsCount || 0 }}</p>
          </div>
          
          <div class="bg-white rounded-lg p-3">
            <p class="text-sm text-indigo-600">计算轨迹长度</p>
            <p class="text-xl font-bold text-indigo-800">{{ (slamInfo.calculatedTrajectoryLength || 0).toFixed(3) }}m</p>
          </div>
          
          <div class="bg-white rounded-lg p-3">
            <p class="text-sm text-indigo-600">3D坐标状态</p>
            <p class="text-xl font-bold text-indigo-800">
              <i class="fa-solid fa-check-circle text-green-500"></i>
              已启用
            </p>
          </div>
        </div>
        
        <!-- 3D轨迹坐标显示 -->
        <div v-if="slamInfo.trajectoryPoints && slamInfo.trajectoryPoints.length > 0" class="mt-4">
          <h5 class="text-sm font-medium text-indigo-700 mb-2">最新轨迹点坐标:</h5>
          <div class="bg-white rounded-lg p-3 max-h-32 overflow-y-auto">
            <div v-for="(point, index) in slamInfo.trajectoryPoints.slice(-5)" :key="index" 
                 class="text-xs text-indigo-600 mb-1">
              帧{{ point.frame_id }}: ({{ (point.x || 0).toFixed(3) }}, {{ (point.y || 0).toFixed(3) }}, {{ (point.z || 0).toFixed(3) }})
            </div>
          </div>
        </div>
      </div>
      
      <!-- 3D SLAM可视化 -->
      <div class="mt-6">
        <SlamVisualization3D 
          ref="slamVisualization3D"
          @slam-data-updated="handleSlamDataUpdated"
        />
      </div>
      
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, reactive } from 'vue'
import axios from 'axios'
import SlamVisualization3D from './SlamVisualization3D.vue'
import { safePlayVideo, setVideoStreamAndPlay } from '../utils/videoPlayManager.js'
import { slamSessionApi, slamDataUtils } from '../services/slamApi.js'

// 响应式数据
const mainVideo = ref(null)
const slamVideo = ref(null)
const keypointCanvas = ref(null)
const loading = ref(false)
const slamVisualization3D = ref(null)
const connecting = ref(false)
const connected = ref(false)
const fullscreen = ref(false)
const connectionStatus = ref('DISCONNECTED') // DISCONNECTED, CONNECTING, CONNECTED, ERROR
const errorMessage = ref('')
// 移除全局SLAM相关变量

// 帧处理相关
const frameCaptureInterval = ref(null)
const frameProcessingEnabled = ref(false)
const frameProcessorUrl = 'http://8.136.52.147:8100'
const processManagerUrl = 'http://8.136.52.147:8110'
const frameSkipCounter = ref(0)
const frameSkipInterval = 0 // 每帧都处理，实时性更好

// 会话管理
const selectedSessionId = ref('')
const availableSessions = ref([])

// 视频状态
const mainVideoPlaying = ref(false)
const slamVideoPlaying = ref(false)
const mainVideoStatus = ref('UNKNOWN')
const slamVideoStatus = ref('UNKNOWN')
const slamProcessing = ref(false)
const slamStatus = ref('STOPPED') // STOPPED, RUNNING, ERROR

// 视频信息
const mainVideoInfo = ref({
  width: 0,
  height: 0,
  fps: 0
})

// SLAM信息
const slamInfo = ref({
  keyframes: 0,
  mapPoints: 0,
  trajectoryLength: 0,
  progress: 0,
  // 增强版SLAM 3D轨迹数据
  algorithm: 'Enhanced SLAM with 3D Trajectory',
  has3DCoordinates: false,
  trajectoryPointsCount: 0,
  calculatedTrajectoryLength: 0,
  trajectoryPoints: []
})

// WebRTC配置 - 参考ShowSchedule.vue的实现
const webrtcConfig = reactive({
  // SRS服务器配置
  srsHost: '8.136.52.147',
  srsApiPort: 1985,
  srsHttpPort: 8080,
  streamName: 'livestream',
  // 视频源选择
  videoSource: 'webrtc://8.136.52.147:8080/live/livestream',
  webrtcSource: 'webrtc://8.136.52.147/live/livestream',
  // SRS WebRTC API端点
  srsApiEndpoints: [
    'http://8.136.52.147:1985/rtc/v1/play/'
  ]
})

// WebRTC相关
let peerConnection = null
let signalingSocket = null
let dataChannel = null
let updateInterval = null

// 方法
const getStatusColor = (status) => {
  const colors = {
    'DISCONNECTED': 'bg-gray-100 text-gray-800',
    'CONNECTING': 'bg-blue-100 text-blue-800',
    'CONNECTED': 'bg-green-100 text-green-800',
    'ERROR': 'bg-red-100 text-red-800'
  }
  return colors[status] || 'bg-gray-100 text-gray-800'
}

const getStatusText = (status) => {
  const texts = {
    'DISCONNECTED': '未连接',
    'CONNECTING': '连接中',
    'CONNECTED': '已连接',
    'ERROR': '连接错误'
  }
  return texts[status] || status
}

const getVideoStatusColor = (status) => {
  const colors = {
    'PLAYING': 'text-green-400',
    'PAUSED': 'text-yellow-400',
    'ERROR': 'text-red-400',
    'UNKNOWN': 'text-gray-400'
  }
  return colors[status] || 'text-gray-400'
}

const getVideoStatusText = (status) => {
  const texts = {
    'PLAYING': '播放中',
    'PAUSED': '已暂停',
    'ERROR': '错误',
    'UNKNOWN': '未知'
  }
  return texts[status] || status
}

const connectWebRTC = async () => {
  // 如果已经连接，不要重复连接
  if (connected.value) {
    console.log('WebRTC已经连接，跳过重复连接')
    return
  }
  
  connecting.value = true
  connectionStatus.value = 'CONNECTING'
  
  try {
    console.log('开始连接视频流:', webrtcConfig.videoSource)
    
    // 确保视频元素存在
    if (!mainVideo.value) {
      console.warn('视频元素不存在，等待初始化...')
      await new Promise(resolve => setTimeout(resolve, 1000))
      if (!mainVideo.value) {
        throw new Error('视频元素初始化失败')
      }
    }
    
    // 尝试连接视频流，最多重试3次
    let retryCount = 0
    const maxRetries = 3
    
    while (retryCount < maxRetries) {
      try {
        console.log(`尝试连接视频流 (第${retryCount + 1}次)`)
        await initializeWebRTC()
        connectionStatus.value = 'CONNECTED'
        connected.value = true
        
        // 不自动启动SLAM信息更新，等待用户手动启动SLAM
        // startSlamInfoUpdate()
        
        console.log('视频流连接成功')
        return // 连接成功，退出重试循环
        
      } catch (videoError) {
        retryCount++
        console.warn(`视频流连接失败 (第${retryCount}次):`, videoError)
        
        if (retryCount < maxRetries) {
          console.log(`等待2秒后重试...`)
          await new Promise(resolve => setTimeout(resolve, 2000))
        } else {
          console.warn('视频流连接失败，不使用模拟流')
          connectionStatus.value = 'ERROR'
          errorMessage.value = '视频流连接失败'
          // 注释掉模拟流，不播放
          // createMockVideoStream()
          // startSlamInfoUpdate()
          
          console.log('已禁用模拟流')
        }
      }
    }
    
  } catch (error) {
    console.error('WebRTC连接失败:', error)
    connectionStatus.value = 'ERROR'
    errorMessage.value = error.message
  } finally {
    connecting.value = false
  }
}

const initializeWebRTC = async () => {
  // 检查是否使用WebRTC流
  if (webrtcConfig.videoSource.startsWith('webrtc://')) {
    console.log('使用WebRTC流连接')
    await connectWebRTCStream()
    return
  }
  
  // 确保视频元素存在
  if (!mainVideo.value) {
    throw new Error('视频元素不存在，无法初始化WebRTC')
  }
  
  // 创建RTCPeerConnection
  const configuration = {
    iceServers: [
      { urls: 'stun:stun.l.google.com:19302' }
    ]
  }
  
  peerConnection = new RTCPeerConnection(configuration)
  
  // 监听远程流
  peerConnection.ontrack = (event) => {
    console.log('收到远程流:', event.streams[0])
    const stream = event.streams[0]
    
    if (mainVideo.value) {
      mainVideo.value.srcObject = stream
      mainVideoStatus.value = 'PLAYING'
      mainVideoPlaying.value = true
    }
    
    // 同时设置SLAM视频流
    if (slamVideo.value) {
      slamVideo.value.srcObject = stream
      slamVideoStatus.value = 'PLAYING'
      slamVideoPlaying.value = true
      console.log('✅ SLAM视频流已设置')
    }
  }
  
  // 创建数据通道用于SLAM数据交换
  dataChannel = peerConnection.createDataChannel('slam-data', {
    ordered: true
  })
  
  dataChannel.onopen = () => {
    console.log('SLAM数据通道已打开')
  }
  
  dataChannel.onmessage = (event) => {
    try {
      const slamData = JSON.parse(event.data)
      updateSlamInfo(slamData)
    } catch (error) {
      console.error('解析SLAM数据失败:', error)
    }
  }
  
  // 创建视频流（从外部视频源）
  try {
    console.log('尝试加载视频源:', webrtcConfig.videoSource)
    
    // 创建视频元素来接收外部流
    const videoElement = document.createElement('video')
    videoElement.src = webrtcConfig.videoSource || 'http://8.136.52.147:8080/live/livestream.flv'
    videoElement.autoplay = true
    videoElement.muted = true
    videoElement.playsInline = true
    videoElement.crossOrigin = 'anonymous' // 允许跨域
    
    // 添加FLV支持
    if (webrtcConfig.videoSource.includes('.flv')) {
      // 对于FLV格式，尝试使用HLS.js或直接播放
      console.log('检测到FLV格式，尝试直接播放')
    }
    
    // 等待视频加载，但不强制超时失败
    await new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        console.warn('视频流加载超时，但继续尝试播放')
        // 不reject，而是resolve，让视频继续尝试播放
        resolve()
      }, 15000) // 增加到15秒超时，给视频更多加载时间
      
      let resolved = false
      
      const resolveOnce = () => {
        if (!resolved) {
          resolved = true
          clearTimeout(timeout)
          console.log('外部视频流加载成功')
          // 延迟一点时间让视频完全加载
          setTimeout(() => {
            resolve()
          }, 1000)
        }
      }
      
      videoElement.onloadedmetadata = () => {
        console.log('视频元数据加载完成')
        resolveOnce()
      }
      
      videoElement.oncanplay = () => {
        console.log('视频可以播放')
        resolveOnce()
      }
      
      videoElement.oncanplaythrough = () => {
        console.log('视频可以流畅播放')
        resolveOnce()
      }
      
      videoElement.onerror = (error) => {
        if (!resolved) {
          resolved = true
          clearTimeout(timeout)
          console.warn('外部视频流加载失败，但继续尝试:', error)
          // 不reject，而是resolve，让系统继续运行
          resolve()
        }
      }
      
      // 添加更多事件监听
      videoElement.onloadstart = () => {
        console.log('开始加载视频')
      }
      
      videoElement.onprogress = () => {
        console.log('视频加载进度更新')
      }
    })
    
    // 创建MediaStream
    const canvas = document.createElement('canvas')
    canvas.width = 640
    canvas.height = 480
    const ctx = canvas.getContext('2d')
    
    // 绘制视频帧到canvas
    const drawFrame = () => {
      if (videoElement.readyState >= 2) { // HAVE_CURRENT_DATA
        ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height)
      }
      requestAnimationFrame(drawFrame)
    }
    
    // 移除重复的onloadedmetadata监听器，避免事件冲突
    // videoElement.onloadedmetadata = () => {
    //   drawFrame()
    // }
    
    const stream = canvas.captureStream(30)
    
    // 设置到视频元素
    if (mainVideo.value) {
      mainVideo.value.srcObject = stream
      mainVideoStatus.value = 'PLAYING'
      mainVideoPlaying.value = true
    }
    
    if (slamVideo.value) {
      slamVideo.value.srcObject = stream
      slamVideoStatus.value = 'PLAYING'
      slamVideoPlaying.value = true
    }
    
    console.log('视频流设置成功')
    
  } catch (error) {
    console.warn('无法加载外部视频流:', error)
    // 注释掉模拟视频流，避免覆盖主视频
    // createMockVideoStream()
    console.log('视频流加载失败，但保持现有视频流')
  }
}

const createMockVideoStream = () => {
  // 创建模拟视频流用于演示
  const canvas = document.createElement('canvas')
  canvas.width = 640
  canvas.height = 480
  const ctx = canvas.getContext('2d')
  
  const drawFrame = () => {
    ctx.fillStyle = '#1a1a1a'
    ctx.fillRect(0, 0, canvas.width, canvas.height)
    
    // 绘制一些模拟的SLAM数据
    ctx.fillStyle = '#00ff00'
    ctx.beginPath()
    ctx.arc(320, 240, 5, 0, 2 * Math.PI)
    ctx.fill()
    
    ctx.fillStyle = '#ff0000'
    ctx.beginPath()
    ctx.arc(300, 200, 3, 0, 2 * Math.PI)
    ctx.fill()
    
    ctx.fillStyle = '#0000ff'
    ctx.beginPath()
    ctx.arc(340, 280, 2, 0, 2 * Math.PI)
    ctx.fill()
    
    requestAnimationFrame(drawFrame)
  }
  
  drawFrame()
  
  const stream = canvas.captureStream(30)
  if (mainVideo.value) {
    mainVideo.value.srcObject = stream
    mainVideoStatus.value = 'PLAYING'
  }
  
  if (slamVideo.value) {
    slamVideo.value.srcObject = stream
    slamVideoStatus.value = 'PLAYING'
  }
}

const connectWebRTCStream = async () => {
  try {
    console.log('连接WebRTC流:', webrtcConfig.webrtcSource)
    
    if (!mainVideo.value) {
      console.error('视频元素不存在，等待元素初始化...')
      // 等待一段时间后重试
      setTimeout(() => {
        if (mainVideo.value) {
          connectWebRTCStream()
        } else {
          console.error('视频元素仍未初始化')
        }
      }, 1000)
      return
    }
    
    const video = mainVideo.value
    
    // 创建RTCPeerConnection - 参考ShowSchedule.vue
    const pc = new RTCPeerConnection({
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' },
        { urls: 'stun:stun1.l.google.com:19302' }
      ]
    })
    
    // 添加接收轨道，这样SDP才会包含媒体信息
    pc.addTransceiver('video', { direction: 'recvonly' })
    pc.addTransceiver('audio', { direction: 'recvonly' })
    
    peerConnection = pc
    
    // 处理远程流
    pc.ontrack = (event) => {
      console.log('收到WebRTC远程流:', event.streams[0])
      
      // 检查流是否有效
      if (!event.streams[0] || event.streams[0].getTracks().length === 0) {
        console.warn('⚠️ 收到的视频流无效或没有轨道')
        return
      }
      
      // 设置视频流
      const stream = event.streams[0]
      video.srcObject = stream
      mainVideoStatus.value = 'PLAYING'
      mainVideoPlaying.value = true
      
      // 同时设置SLAM视频流
      if (slamVideo.value) {
        slamVideo.value.srcObject = stream
        slamVideoStatus.value = 'PLAYING'
        slamVideoPlaying.value = true
        console.log('✅ 外部视频流SLAM视频已设置')
      }
      
      // 添加流状态监听
      stream.getTracks().forEach(track => {
        console.log('视频轨道状态:', track.kind, track.readyState)
        track.onended = () => {
          console.warn('视频轨道结束:', track.kind)
        }
      })
      
      // 确保视频开始播放，添加重试机制
      const tryPlay = (attempt = 1) => {
        // 先暂停避免并发播放请求
        video.pause()
        setTimeout(() => {
          video.play().then(() => {
          console.log('✅ WebRTC视频开始播放')
          mainVideoPlaying.value = true
          
          // 验证视频是否真的在播放
          setTimeout(() => {
            if (video.paused) {
              console.warn('⚠️ 视频播放后仍然暂停，尝试重新播放')
              video.play().catch(err => console.error('重新播放失败:', err))
            } else {
              console.log('✅ 视频确认正在播放')
            }
          }, 1000)
        }).catch(error => {
          console.error(`❌ WebRTC视频播放失败 (第${attempt}次):`, error)
          
          if (attempt < 3) {
            // 重试播放
            setTimeout(() => {
              console.log(`🔄 重试播放视频 (第${attempt + 1}次)...`)
              tryPlay(attempt + 1)
            }, 1000)
          } else {
            // 尝试静音播放
            console.log('🔄 尝试静音播放...')
            video.muted = true
            video.play().then(() => {
              console.log('✅ WebRTC静音视频开始播放')
              mainVideoPlaying.value = true
            }).catch(err => {
              console.error('❌ WebRTC静音视频播放也失败:', err)
              mainVideoStatus.value = 'ERROR'
            })
          }
        })
        }, 100) // 100ms延迟
      }
      
      tryPlay()
    }
    
    // 处理ICE连接状态
    pc.oniceconnectionstatechange = () => {
      console.log('ICE连接状态:', pc.iceConnectionState)
      if (pc.iceConnectionState === 'connected') {
        connectionStatus.value = 'CONNECTED'
        connected.value = true
        console.log('✅ WebRTC连接成功')
        // 暂时禁用连接状态监控，避免误判断开
        // startConnectionMonitoring()
        
        // 强制播放视频
        setTimeout(() => {
          forcePlayVideo()
        }, 1000)
        
        // 启动简化的视频状态检查
        startSimpleVideoCheck()
        
        // 等待视频稳定，但不自动启动帧捕获
        setTimeout(() => {
          console.log('视频连接稳定，等待用户手动启动SLAM...')
          // 不自动启动帧捕获，等待用户手动点击开始SLAM按钮
        }, 2000)
      } else if (pc.iceConnectionState === 'failed') {
        console.error('❌ WebRTC连接失败')
        connectionStatus.value = 'ERROR'
        connected.value = false
        
        // 添加自动重连机制
        setTimeout(() => {
          console.log('🔄 尝试自动重连...')
          reconnect()
        }, 3000)
      } else if (pc.iceConnectionState === 'disconnected') {
        console.warn('⚠️ WebRTC连接断开')
        connectionStatus.value = 'DISCONNECTED'
        connected.value = false
        // 添加自动重连机制
        setTimeout(() => {
          console.log('🔄 连接断开，尝试重连...')
          reconnect()
        }, 2000)
      }
    }
    
    // 创建offer
    const offer = await pc.createOffer()
    
    // 修改SDP以支持BUNDLE - 参考ShowSchedule.vue
    let sdp = offer.sdp
    if (!sdp.includes('a=group:BUNDLE')) {
      const lines = sdp.split('\n')
      const mLineIndex = lines.findIndex(line => line.startsWith('m='))
      if (mLineIndex !== -1) {
        lines.splice(mLineIndex, 0, 'a=group:BUNDLE 0')
        sdp = lines.join('\n')
      }
    }
    
    const modifiedOffer = new RTCSessionDescription({
      type: 'offer',
      sdp: sdp
    })
    
    await pc.setLocalDescription(modifiedOffer)
    
    // 使用SRS WebRTC API - 参考ShowSchedule.vue
    const requestData = {
      api: webrtcConfig.srsApiEndpoints[0],
      streamurl: webrtcConfig.webrtcSource,
      sdp: offer.sdp
    }
    
    console.log('发送到SRS的请求数据:', requestData)
    
    let response = null
    let lastError = null
    
    // 尝试多个API端点
    for (const endpoint of webrtcConfig.srsApiEndpoints) {
      try {
        console.log(`尝试SRS API端点: ${endpoint}`)
        
        response = await fetch(endpoint, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(requestData)
        })
        
        console.log(`SRS API端点 ${endpoint} 响应状态:`, response.status)
        
        if (response.ok) {
          const data = await response.json()
          console.log(`SRS API端点 ${endpoint} 响应数据:`, data)
          
          if (data.code === 0) {
            console.log('✅ SRS服务器返回成功，设置远程描述...')
            await pc.setRemoteDescription(new RTCSessionDescription({
              type: 'answer',
              sdp: data.sdp
            }))
            console.log(`✅ SRS API端点 ${endpoint} 连接成功`)
            return // 成功连接，退出函数
          } else {
            console.error(`❌ SRS服务器返回错误: code=${data.code}, message=${data.message || '未知错误'}`)
            lastError = new Error(`SRS API端点 ${endpoint} 响应错误: ${data.code} - ${data.message || '未知错误'}`)
          }
        } else {
          const errorText = await response.text()
          console.error(`❌ HTTP错误: ${response.status}, 响应: ${errorText}`)
          lastError = new Error(`SRS API端点 ${endpoint} HTTP错误: ${response.status} - ${errorText}`)
        }
      } catch (error) {
        console.error(`SRS API端点 ${endpoint} 请求失败:`, error)
        lastError = error
      }
    }
    
    // 如果所有API端点都失败
    throw lastError || new Error('所有SRS API端点连接失败')
    
  } catch (error) {
    console.error('WebRTC流连接失败:', error)
    throw error
  }
}

// 检查SRS服务器状态 - 参考ShowSchedule.vue
const checkSRSServer = async () => {
  try {
    console.log('检查SRS服务器状态...')
    
    // 检查各个端口的连通性
    const ports = [
      { port: 1985, name: 'SRS API端口' },
      { port: 8080, name: 'HLS端口' },
      { port: 1935, name: 'RTMP端口' },
      { port: 80, name: 'HTTP端口' }
    ]
    
    const connectivityResults = {}
    
    // 并行检查端口连通性
    const connectivityPromises = ports.map(async ({ port, name }) => {
      try {
        const response = await fetch(`http://${webrtcConfig.srsHost}:${port}`, {
          method: 'HEAD',
          timeout: 3000
        })
        connectivityResults[port] = response.ok
        console.log(`${name} (${port}): ${response.ok ? '✅ 可访问' : '❌ 不可访问'}`)
      } catch (error) {
        connectivityResults[port] = false
        console.log(`${name} (${port}): ❌ 不可访问 - ${error.message}`)
      }
    })
    
    await Promise.all(connectivityPromises)
    
    // 检查流状态
    let streamsData = null
    if (connectivityResults[1985]) {
      try {
        const streamsResponse = await fetch(`http://${webrtcConfig.srsHost}:${webrtcConfig.srsApiPort}/rtc/v1/streams/`, {
          method: 'GET',
          timeout: 5000
        })
        streamsData = await streamsResponse.json()
        console.log('SRS服务器流状态:', streamsData)
      } catch (error) {
        console.warn('无法获取流状态:', error)
      }
    }
    
    return {
      connectivity: connectivityResults,
      streams: streamsData
    }
    
  } catch (error) {
    console.error('检查SRS服务器失败:', error)
    return null
  }
}

// 启动SLAM信息更新 - 连接真正的SLAM后端服务
const startSlamInfoUpdate = () => {
  if (updateInterval) {
    clearInterval(updateInterval)
  }
  
  console.log('🎯 启动SLAM信息更新')
  
  updateInterval = setInterval(async () => {
    try {
      // 检查SLAM是否在运行
      if (!slamProcessing.value) {
        console.log('⚠️ 没有SLAM在运行，跳过数据更新')
        return
      }
      
      console.log('🔍 正在获取SLAM会话数据...')
      
      // 检查是否有选中的会话
      if (!selectedSessionId.value) {
        console.log('⚠️ 没有选中会话，跳过数据更新')
        return
      }
      
      // 获取会话状态
      const statusResult = await slamSessionApi.getSessionStatus(selectedSessionId.value)
      if (statusResult.success && statusResult.data.success) {
        const statusData = slamDataUtils.formatStats(statusResult.data.status)
        console.log('📊 SLAM状态数据:', statusData)
        
        // 更新SLAM信息
        slamInfo.value.keyframes = statusData.keyframes || 0
        slamInfo.value.mapPoints = statusData.mapPoints || 0
        slamInfo.value.trajectoryLength = statusData.trajectoryLength || 0
        slamInfo.value.progress = Math.min((statusData.framesProcessed / Math.max(statusData.framesReceived, 1)) * 100, 100)
        
        // 更新增强版SLAM 3D轨迹数据
        slamInfo.value.algorithm = 'Enhanced SLAM with Session Management'
        slamInfo.value.has3DCoordinates = true
        slamInfo.value.trajectoryPointsCount = statusData.trajectoryPoints?.length || 0
        slamInfo.value.calculatedTrajectoryLength = statusData.trajectoryLength || 0
        
        console.log('更新后SLAM信息:', slamInfo.value)
        console.log('✅ SLAM数据更新成功')
        
        // 更新3D可视化
        if (slamVisualization3D.value && typeof slamVisualization3D.value.updateSlamData === 'function') {
          try {
            slamVisualization3D.value.updateSlamData(slamInfo.value)
          } catch (error) {
            console.error('❌ 3D可视化更新失败:', error)
          }
        }
        
        // 绘制视频上的关键点框
        setTimeout(() => {
          drawKeypoints()
        }, 100)
        
      } else {
        console.warn('⚠️ 获取SLAM状态失败:', statusResult.data?.message || statusResult.error)
      }
      
      // 获取会话数据（包含轨迹点）
      const dataResult = await slamSessionApi.getSessionData(selectedSessionId.value)
      if (dataResult.success && dataResult.data.success) {
        const sessionData = dataResult.data.data
        if (sessionData.trajectory) {
          // 更新轨迹点数据
          slamInfo.value.trajectoryPoints = sessionData.trajectory.trajectoryPoints || []
          
          // 更新3D可视化轨迹
          if (slamVisualization3D.value && typeof slamVisualization3D.value.updateSlamData === 'function') {
            slamVisualization3D.value.updateSlamData(slamInfo.value)
          }
        }
      }
      
    } catch (error) {
      console.error('❌ 获取SLAM数据失败:', error)
    }
  }, 1000) // 每1秒更新一次，减少API调用频率
}

// 更新模拟SLAM数据（作为备选方案）- 已禁用
const updateSimulatedSlamData = () => {
  // 注释掉模拟数据，不播放
  // console.log('🔄 使用模拟SLAM数据')
  // 模拟真实的SLAM数据增长
  // slamInfo.value.keyframes = Math.floor(Math.random() * 50) + 10
  // slamInfo.value.mapPoints = Math.floor(Math.random() * 1000) + 100
  // slamInfo.value.trajectoryLength = Math.random() * 10 + 1
  // slamInfo.value.progress = Math.min(slamInfo.value.progress + Math.random() * 1, 100)
  // console.log('模拟数据更新:', slamInfo.value)
  console.log('模拟数据更新已禁用')
}

const disconnectWebRTC = () => {
  // 停止连接监控
  stopConnectionMonitoring()
  // 停止视频流监控
  stopVideoStreamMonitoring()
  
  if (signalingSocket) {
    signalingSocket.close()
    signalingSocket = null
  }
  
  if (peerConnection) {
    peerConnection.close()
    peerConnection = null
  }
  
  if (dataChannel) {
    dataChannel.close()
    dataChannel = null
  }
  
  connected.value = false
  connectionStatus.value = 'DISCONNECTED'
  
  // 停止视频流
  if (mainVideo.value) {
    mainVideo.value.srcObject = null
    mainVideoStatus.value = 'UNKNOWN'
  }
  
  if (slamVideo.value) {
    slamVideo.value.srcObject = null
    slamVideoStatus.value = 'UNKNOWN'
  }
}

// 强制播放视频
const forcePlayVideo = async () => {
  if (mainVideo.value && mainVideo.value.srcObject) {
    // 检查视频是否已经在播放，避免重复操作
    if (mainVideo.value.readyState >= 2 && !mainVideo.value.paused) {
      console.log('✅ 视频已在播放，跳过重复播放')
      return
    }
    
    try {
      // 先暂停再播放，确保视频重新开始
      mainVideo.value.pause()
      await new Promise(resolve => setTimeout(resolve, 100))
      
      await mainVideo.value.play()
      mainVideoPlaying.value = true
      mainVideoStatus.value = 'PLAYING'
      console.log('✅ 强制播放主视频成功')
      
      // 同时播放SLAM视频
      if (slamVideo.value) {
        slamVideo.value.srcObject = mainVideo.value.srcObject
        slamVideo.value.pause()
        await new Promise(resolve => setTimeout(resolve, 100))
        
        await slamVideo.value.play()
        slamVideoPlaying.value = true
        slamVideoStatus.value = 'PLAYING'
        console.log('✅ 强制播放SLAM视频成功')
      }
    } catch (error) {
      console.error('❌ 强制播放视频失败:', error)
      // 只有在真正无法播放时才重连，避免误判
      if (error.name === 'NotAllowedError' || error.name === 'NotSupportedError') {
        console.log('🔄 视频播放权限或格式问题，尝试重新连接...')
        setTimeout(() => {
          reconnect()
        }, 3000)
      } else {
        console.warn('⚠️ 视频播放失败，但不重连:', error.name)
      }
    }
  } else {
    console.warn('⚠️ 视频元素或流不存在，尝试重新连接...')
    setTimeout(() => {
      reconnect()
    }, 2000)
  }
}

const toggleMainVideo = () => {
  if (mainVideo.value) {
    if (mainVideoPlaying.value) {
      mainVideo.value.pause()
      mainVideoStatus.value = 'PAUSED'
    } else {
      mainVideo.value.play()
      mainVideoStatus.value = 'PLAYING'
    }
    mainVideoPlaying.value = !mainVideoPlaying.value
  }
}

const toggleSlamVideo = () => {
  if (slamVideo.value) {
    if (slamVideoPlaying.value) {
      // 暂停SLAM视频播放
      slamVideo.value.pause()
      slamVideoStatus.value = 'PAUSED'
      slamVideoPlaying.value = false
      console.log('SLAM视频已暂停')
    } else {
      // 恢复SLAM视频播放，但不重新加载流
      try {
        // 检查视频是否已经有源
        if (slamVideo.value.srcObject || slamVideo.value.src) {
          slamVideo.value.play()
          slamVideoStatus.value = 'PLAYING'
          slamVideoPlaying.value = true
          console.log('SLAM视频已恢复播放')
        } else {
          console.warn('SLAM视频没有源，尝试设置视频源')
          // 如果SLAM视频没有源，尝试从主视频复制流
          if (mainVideo.value && mainVideo.value.srcObject) {
            slamVideo.value.srcObject = mainVideo.value.srcObject
            slamVideo.value.play()
            slamVideoStatus.value = 'PLAYING'
            slamVideoPlaying.value = true
            console.log('SLAM视频已设置源并开始播放')
          } else {
            console.warn('主视频也没有源，无法播放SLAM视频')
            slamVideoStatus.value = 'ERROR'
          }
        }
      } catch (error) {
        console.warn('SLAM视频播放失败:', error)
        // 如果播放失败，不改变状态
        slamVideoStatus.value = 'ERROR'
      }
    }
  } else {
    console.warn('SLAM视频元素不存在')
  }
}

const startSlamProcessing = async () => {
  try {
    console.log('🚀 自动启动SLAM处理...')
    
    // 检查WebRTC连接状态
    if (!connected.value) {
      console.warn('WebRTC未连接，无法启动SLAM')
      return
    }
    
    // 确保视频正在播放
    if (mainVideo.value && mainVideo.value.paused) {
      try {
        await mainVideo.value.play()
        console.log('视频已开始播放')
      } catch (error) {
        console.warn('无法播放视频:', error)
        return
      }
    }
    
    // 启动SLAM信息更新
    startSlamInfoUpdate()
    
    slamProcessing.value = true
    slamStatus.value = 'RUNNING'
    
    console.log('✅ SLAM处理已自动启动')
  } catch (error) {
    console.error('自动启动SLAM处理失败:', error)
    slamProcessing.value = false
    slamStatus.value = 'ERROR'
  }
}

const stopSlamProcessing = async () => {
  slamProcessing.value = false
  
  try {
    await axios.post('/api/stream-slam/stop', {
      sessionId: 1
    })
  } catch (error) {
    console.error('停止SLAM处理失败:', error)
  }
  
  if (updateInterval) {
    clearInterval(updateInterval)
    updateInterval = null
  }
  
  // 停止帧捕获
  if (frameCaptureInterval.value) {
    clearInterval(frameCaptureInterval.value)
    frameCaptureInterval.value = null
    frameProcessingEnabled.value = false
    console.log('✅ 帧捕获已停止')
  }
  
  // 停止发送视频帧
  stopVideoFrameSending()
}

// 视频帧发送相关
let videoFrameInterval = null
let canvas = null
let ctx = null

// 关键点绘制相关
let keypointInterval = null

// 开始发送视频帧到后端
const startVideoFrameSending = () => {
  console.log('📡 开始发送视频帧到后端...')
  
  // 创建canvas用于捕获视频帧
  if (!canvas) {
    canvas = document.createElement('canvas')
    ctx = canvas.getContext('2d')
  }
  
  // 每200ms发送一帧（5fps），减少数据传输量
  videoFrameInterval = setInterval(() => {
    sendVideoFrame()
  }, 200)
}

// 停止发送视频帧
const stopVideoFrameSending = () => {
  console.log('🛑 停止发送视频帧...')
  if (videoFrameInterval) {
    clearInterval(videoFrameInterval)
    videoFrameInterval = null
  }
}

// 发送视频帧到后端进行真实SLAM处理
const sendVideoFrame = async () => {
  try {
    if (!mainVideo.value || !mainVideo.value.videoWidth || !mainVideo.value.videoHeight) {
      return
    }
    
    // 检查SLAM是否在运行
    if (!slamProcessing.value || !selectedSessionId.value) {
      console.log('⚠️ 没有SLAM在运行或没有选中会话，跳过视频帧发送')
      return
    }
    
    // 设置canvas尺寸
    canvas.width = mainVideo.value.videoWidth
    canvas.height = mainVideo.value.videoHeight
    
    // 将视频帧绘制到canvas
    ctx.drawImage(mainVideo.value, 0, 0, canvas.width, canvas.height)
    
    // 转换为base64
    const frameData = slamDataUtils.captureFrameToBase64(mainVideo.value, canvas)
    if (!frameData) {
      console.warn('⚠️ 帧捕获失败')
      return
    }
    
    try {
      // 发送帧到SLAM会话
      const result = await slamSessionApi.addFrame(selectedSessionId.value, frameData)
      if (result.success && result.data.success) {
        console.log('✅ 帧发送成功')
        
        // 更新SLAM数据，包括关键点
        if (result.data.keypoints && Array.isArray(result.data.keypoints)) {
          slamInfo.value.keypoints = result.data.keypoints
          slamInfo.value.matches = result.data.matches || []
          console.log('🎯 更新关键点数据:', result.data.keypoints.length, '个关键点')
        }
        
        // 更新其他SLAM数据
        if (result.data.keyframes !== undefined) {
          slamInfo.value.keyframes = result.data.keyframes
        }
        if (result.data.map_points !== undefined) {
          slamInfo.value.mapPoints = result.data.map_points
        }
        if (result.data.trajectory_length !== undefined) {
          slamInfo.value.trajectoryLength = result.data.trajectory_length
        }
      } else {
        console.warn('⚠️ 帧发送失败:', result.data?.message || result.error)
      }
    } catch (error) {
      console.error('❌ 发送视频帧错误:', error)
    }
  } catch (error) {
    console.error('❌ 发送视频帧错误:', error)
  }
}

// 从真实SLAM处理结果更新数据
const updateSlamDataFromRealProcessing = (data) => {
  if (data) {
    slamInfo.value.keyFrames = data.keyframes || slamInfo.value.keyFrames
    slamInfo.value.mapPoints = data.mapPoints || slamInfo.value.mapPoints
    slamInfo.value.trajectoryLength = data.trajectoryLength || slamInfo.value.trajectoryLength
    slamInfo.value.progress = data.progress || slamInfo.value.progress
    
    console.log('📊 更新真实SLAM数据:', {
      keyframes: slamInfo.value.keyFrames,
      mapPoints: slamInfo.value.mapPoints,
      trajectoryLength: slamInfo.value.trajectoryLength,
      progress: slamInfo.value.progress
    })
    
    // 更新3D可视化
    if (slamVisualization3D.value && typeof slamVisualization3D.value.updateSlamData === 'function') {
      try {
        slamVisualization3D.value.updateSlamData({
          keyframes: slamInfo.value.keyFrames,
          mapPoints: slamInfo.value.mapPoints,
          trajectoryLength: slamInfo.value.trajectoryLength,
          progress: slamInfo.value.progress
        })
      } catch (error) {
        console.error('❌ 3D可视化更新失败:', error)
      }
    } else {
      console.warn('⚠️ 3D可视化组件未初始化或方法不存在')
    }
  }
}

// 处理3D可视化的SLAM数据更新
const handleSlamDataUpdated = (data) => {
  console.log('🎯 3D可视化SLAM数据更新:', data)
  // 可以在这里添加额外的处理逻辑
}

// 测试绘制功能
const testKeypointDrawing = () => {
  if (!keypointCanvas.value) {
    console.error('❌ 测试失败: keypointCanvas 不存在')
    return
  }
  
  const canvas = keypointCanvas.value
  const ctx = canvas.getContext('2d')
  
  // 设置canvas尺寸
  canvas.width = 320
  canvas.height = 240
  
  // 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  
  // 绘制测试关键点
  ctx.fillStyle = '#ff0000' // 红色测试点
  ctx.strokeStyle = '#ff0000'
  ctx.lineWidth = 3
  
  // 在四个角落绘制测试点
  const testPoints = [
    [50, 50], [270, 50], [50, 190], [270, 190], [160, 120]
  ]
  
  testPoints.forEach(([x, y]) => {
    ctx.beginPath()
    ctx.arc(x, y, 6, 0, 2 * Math.PI)
    ctx.fill()
    ctx.stroke()
  })
  
  console.log('🧪 测试绘制完成，应该看到5个红色圆点')
}

// 显示后端返回的处理后图片
const displayProcessedFrame = () => {
  if (!keypointCanvas.value || !slamInfo.value.processedFrame) {
    console.warn('⚠️ 无法显示处理后图片: canvas或图片数据不存在')
    return
  }
  
  const canvas = keypointCanvas.value
  const ctx = canvas.getContext('2d')
  
  // 创建图片对象
  const img = new Image()
  img.onload = () => {
    // 设置canvas尺寸
    canvas.width = img.width
    canvas.height = img.height
    
    // 绘制图片
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    ctx.drawImage(img, 0, 0)
    
    console.log('✅ 显示处理后图片:', {
      size: `${img.width}x${img.height}`,
      keypoints: slamInfo.value.keypoints?.length || 0,
      matches: slamInfo.value.matches?.length || 0
    })
  }
  img.onerror = () => {
    console.error('❌ 加载处理后图片失败')
  }
  img.src = slamInfo.value.processedFrame
}

// 绘制关键点和匹配
const drawKeypointsAndMatches = () => {
  if (!keypointCanvas.value) {
    console.warn('⚠️ keypointCanvas 不存在')
    return
  }
  
  const canvas = keypointCanvas.value
  const ctx = canvas.getContext('2d')
  
  // 获取视频尺寸
  const videoWidth = slamVideo.value?.videoWidth || slamVideo.value?.clientWidth || 320
  const videoHeight = slamVideo.value?.videoHeight || slamVideo.value?.clientHeight || 240
  
  // 设置canvas尺寸与视频匹配
  canvas.width = videoWidth
  canvas.height = videoHeight
  
  console.log('🎨 绘制关键点:', {
    canvasSize: `${canvas.width}x${canvas.height}`,
    keypointsCount: slamInfo.value.keypoints?.length || 0,
    matchesCount: slamInfo.value.matches?.length || 0
  })
  
  // 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  
  // 绘制关键点（增强版）
  if (slamInfo.value.keypoints && slamInfo.value.keypoints.length > 0) {
    const keypoints = slamInfo.value.keypoints
    const maxKeypoints = Math.min(keypoints.length, 30) // 主窗口显示最多30个关键点
    
    keypoints.slice(0, maxKeypoints).forEach((point, index) => {
      if (point && point.length === 2) {
        const [x, y] = point
        // 确保坐标在canvas范围内
        if (x >= 0 && x < canvas.width && y >= 0 && y < canvas.height) {
          // 根据关键点重要性设置不同样式
          const isImportant = index < 10 // 前10个关键点更重要
          const radius = isImportant ? 3 : 2 // 减小关键点大小
          const color = isImportant ? '#ff0000' : '#00ff00'
          const strokeColor = isImportant ? '#ffffff' : '#00ff00'
          
          // 绘制发光效果
          ctx.fillStyle = color + '30'
          ctx.beginPath()
          ctx.arc(x, y, radius + 3, 0, 2 * Math.PI)
          ctx.fill()
          
          // 绘制关键点主体
          ctx.fillStyle = color
          ctx.strokeStyle = strokeColor
          ctx.lineWidth = 2
          ctx.beginPath()
          ctx.arc(x, y, radius, 0, 2 * Math.PI)
          ctx.fill()
          ctx.stroke()
          
          // 绘制关键点编号（只对重要关键点）
          if (isImportant) {
            ctx.fillStyle = strokeColor
            ctx.font = 'bold 10px Arial'
            ctx.strokeStyle = '#000000'
            ctx.lineWidth = 1
            const text = `${index + 1}`
            ctx.strokeText(text, x + radius + 2, y - radius - 2)
            ctx.fillText(text, x + radius + 2, y - radius - 2)
          }
        }
      }
    })
    
    // 绘制轨迹线（蓝色）
    console.log('🔍 主窗口检查轨迹数据:', {
      hasTrajectoryPoints: !!slamInfo.value.trajectoryPoints,
      trajectoryLength: slamInfo.value.trajectoryPoints?.length || 0,
      firstPoint: slamInfo.value.trajectoryPoints?.[0]
    })
    
    if (slamInfo.value.trajectoryPoints && slamInfo.value.trajectoryPoints.length > 1) {
      ctx.strokeStyle = '#0066ff'
      ctx.lineWidth = 4
      ctx.beginPath()
      
      const trajectoryPoints = slamInfo.value.trajectoryPoints
      let validPoints = 0
      
      for (let i = 0; i < trajectoryPoints.length; i++) {
        const point = trajectoryPoints[i]
        if (point && typeof point.x === 'number' && typeof point.y === 'number') {
          // 将3D坐标转换为2D屏幕坐标
          const screenX = (point.x + 5) * 40 + canvas.width / 2
          const screenY = (point.y + 5) * 40 + canvas.height / 2
          
          if (i === 0) {
            ctx.moveTo(screenX, screenY)
          } else {
            ctx.lineTo(screenX, screenY)
          }
          validPoints++
        }
      }
      
      if (validPoints > 1) {
        ctx.stroke()
        console.log(`🔵 主窗口绘制了 ${validPoints} 个有效轨迹点`)
      }
      
      // 绘制轨迹点
      ctx.fillStyle = '#0066ff'
      trajectoryPoints.forEach((point, index) => {
        if (point && typeof point.x === 'number' && typeof point.y === 'number') {
          const screenX = (point.x + 5) * 40 + canvas.width / 2
          const screenY = (point.y + 5) * 40 + canvas.height / 2
          
          if (screenX >= 0 && screenX < canvas.width && screenY >= 0 && screenY < canvas.height) {
            ctx.beginPath()
            ctx.arc(screenX, screenY, 3, 0, 2 * Math.PI)
            ctx.fill()
          }
        }
      })
    } else {
      console.log('⚠️ 主窗口没有足够的轨迹数据绘制轨迹线')
    }
    
    // 绘制关键点统计信息
    ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'
    ctx.fillRect(10, 10, 150, 30)
    ctx.fillStyle = '#00ff00'
    ctx.font = 'bold 14px Arial'
    ctx.fillText(`关键点: ${maxKeypoints}/${keypoints.length}`, 15, 30)
    
    console.log('✅ 绘制了', maxKeypoints, '个关键点')
  } else {
    console.warn('⚠️ 没有关键点数据')
  }
  
  // 绘制匹配线（增强版）- 限制数量提高性能
  if (slamInfo.value.matches && slamInfo.value.matches.length > 0) {
    const matches = slamInfo.value.matches
    const maxMatches = Math.min(matches.length, 10) // 限制到10条线提高性能
    
    // 绘制匹配线
    ctx.strokeStyle = 'rgba(0, 136, 255, 0.6)'
    ctx.lineWidth = 1
    
    matches.slice(0, maxMatches).forEach((match, index) => {
      if (match && match.length === 4) {
        const [x1, y1, x2, y2] = match
        // 确保坐标在canvas范围内
        if (x1 >= 0 && x1 < canvas.width && y1 >= 0 && y1 < canvas.height &&
            x2 >= 0 && x2 < canvas.width && y2 >= 0 && y2 < canvas.height) {
          ctx.beginPath()
          ctx.moveTo(x1, y1)
          ctx.lineTo(x2, y2)
          ctx.stroke()
        }
      }
    })
    
    // 绘制匹配统计信息
    ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'
    ctx.fillRect(10, 45, 150, 20)
    ctx.fillStyle = '#0088ff'
    ctx.font = 'bold 12px Arial'
    ctx.fillText(`匹配: ${maxMatches}/${matches.length}`, 15, 58)
    
    console.log('✅ 绘制了', maxMatches, '条匹配线')
  } else {
    console.warn('⚠️ 没有匹配数据')
  }
}

// 绘制关键点绿色框
const drawKeypoints = () => {
  if (!keypointCanvas.value || !slamVideo.value) return
  
  // 检查SLAM是否在运行
  if (!slamProcessing.value) {
    console.log('⚠️ SLAM已停止，跳过关键点绘制')
    // 清除画布
    const canvas = keypointCanvas.value
    const ctx = canvas.getContext('2d')
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    return
  }
  
  const canvas = keypointCanvas.value
  const ctx = canvas.getContext('2d')
  const video = slamVideo.value
  
  // 设置canvas尺寸与小视频窗口匹配
  canvas.width = video.clientWidth || 320
  canvas.height = video.clientHeight || 240
  
  // 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  
  // 使用真实的关键点数据
  if (slamInfo.value.keypoints && slamInfo.value.keypoints.length > 0) {
    const keypoints = slamInfo.value.keypoints
    const maxKeypoints = Math.min(keypoints.length, 30) // 显示最多30个关键点
    
    // 绘制关键点连接线（如果有匹配信息）- 限制数量提高性能
    if (slamInfo.value.matches && slamInfo.value.matches.length > 0) {
      ctx.strokeStyle = 'rgba(0, 255, 0, 0.3)'
      ctx.lineWidth = 1
      ctx.beginPath()
      
      const maxMatches = Math.min(slamInfo.value.matches.length, 5) // 减少到5条线
      for (let i = 0; i < maxMatches; i++) {
        const match = slamInfo.value.matches[i]
        if (match && match.length === 4) {
          const [x1, y1, x2, y2] = match
          if (x1 >= 0 && x1 < canvas.width && y1 >= 0 && y1 < canvas.height &&
              x2 >= 0 && x2 < canvas.width && y2 >= 0 && y2 < canvas.height) {
            ctx.moveTo(x1, y1)
            ctx.lineTo(x2, y2)
          }
        }
      }
      ctx.stroke()
    }
    
    // 绘制关键点
    for (let i = 0; i < maxKeypoints; i++) {
      const point = keypoints[i]
      if (point && point.length === 2) {
        const [x, y] = point
        
        // 确保坐标在画布范围内
        if (x >= 0 && x < canvas.width && y >= 0 && y < canvas.height) {
          // 根据关键点重要性设置不同颜色和大小
          const isImportant = i < 5 // 前5个关键点更重要
          const radius = isImportant ? 3 : 2 // 减小关键点大小
          const color = isImportant ? '#ff0000' : '#00ff00' // 重要关键点用红色
          const strokeColor = isImportant ? '#ffffff' : '#00ff00'
          
          // 绘制关键点外圈（发光效果）
          ctx.fillStyle = color + '40' // 半透明
          ctx.beginPath()
          ctx.arc(x, y, radius + 2, 0, 2 * Math.PI)
          ctx.fill()
          
          // 绘制关键点主体
          ctx.fillStyle = color
          ctx.strokeStyle = strokeColor
          ctx.lineWidth = 2
          ctx.beginPath()
          ctx.arc(x, y, radius, 0, 2 * Math.PI)
          ctx.fill()
          ctx.stroke()
          
          // 绘制关键点编号
          ctx.fillStyle = strokeColor
          ctx.font = `bold ${isImportant ? '12px' : '10px'} Arial`
          ctx.strokeStyle = '#000000'
          ctx.lineWidth = 2
          const text = `KF${i + 1}`
          const textX = x + radius + 3
          const textY = y - radius - 3
          
          // 绘制文字描边
          ctx.strokeText(text, textX, textY)
          // 绘制文字填充
          ctx.fillText(text, textX, textY)
        }
      }
    }
    
    // 绘制轨迹线（蓝色）
    console.log('🔍 检查轨迹数据:', {
      hasTrajectoryPoints: !!slamInfo.value.trajectoryPoints,
      trajectoryLength: slamInfo.value.trajectoryPoints?.length || 0,
      firstPoint: slamInfo.value.trajectoryPoints?.[0],
      slamInfoKeys: Object.keys(slamInfo.value)
    })
    
    if (slamInfo.value.trajectoryPoints && slamInfo.value.trajectoryPoints.length > 1) {
      ctx.strokeStyle = '#0066ff'
      ctx.lineWidth = 3
      ctx.beginPath()
      
      const trajectoryPoints = slamInfo.value.trajectoryPoints
      let validPoints = 0
      
      for (let i = 0; i < trajectoryPoints.length; i++) {
        const point = trajectoryPoints[i]
        if (point && typeof point.x === 'number' && typeof point.y === 'number') {
          // 将3D坐标转换为2D屏幕坐标（这里需要根据实际情况调整）
          const screenX = (point.x + 5) * 30 + canvas.width / 2 // 简单的坐标转换
          const screenY = (point.y + 5) * 30 + canvas.height / 2
          
          if (i === 0) {
            ctx.moveTo(screenX, screenY)
          } else {
            ctx.lineTo(screenX, screenY)
          }
          validPoints++
        }
      }
      
      if (validPoints > 1) {
        ctx.stroke()
        console.log(`🔵 绘制了 ${validPoints} 个有效轨迹点`)
      }
      
      // 绘制轨迹点
      ctx.fillStyle = '#0066ff'
      trajectoryPoints.forEach((point, index) => {
        if (point && typeof point.x === 'number' && typeof point.y === 'number') {
          const screenX = (point.x + 5) * 30 + canvas.width / 2
          const screenY = (point.y + 5) * 30 + canvas.height / 2
          
          if (screenX >= 0 && screenX < canvas.width && screenY >= 0 && screenY < canvas.height) {
            ctx.beginPath()
            ctx.arc(screenX, screenY, 2, 0, 2 * Math.PI)
            ctx.fill()
          }
        }
      })
    } else {
      console.log('⚠️ 没有足够的轨迹数据绘制轨迹线')
    }
    
    // 绘制关键点统计信息
    ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'
    ctx.fillRect(5, 5, 120, 25)
    ctx.fillStyle = '#00ff00'
    ctx.font = 'bold 12px Arial'
    ctx.fillText(`关键点: ${maxKeypoints}/${keypoints.length}`, 10, 20)
    
    console.log(`🎯 在小视频窗口绘制了 ${maxKeypoints} 个真实关键点`)
  } else {
    console.log('🎯 没有关键点数据可绘制')
  }
}

// 清除关键点
const clearKeypoints = () => {
  if (!keypointCanvas.value) return
  
  const canvas = keypointCanvas.value
  const ctx = canvas.getContext('2d')
  ctx.clearRect(0, 0, canvas.width, canvas.height)
}

// 开始关键点绘制
const startKeypointDrawing = () => {
  console.log('🎯 开始关键点绘制...')
  
  // 每500ms更新一次关键点，减少绘制频率
  keypointInterval = setInterval(() => {
    // 只有在SLAM运行时才绘制关键点
    if (slamProcessing.value) {
      drawKeypoints()
    }
  }, 500)
}

// 停止关键点绘制
const stopKeypointDrawing = () => {
  console.log('🛑 停止关键点绘制...')
  if (keypointInterval) {
    clearInterval(keypointInterval)
    keypointInterval = null
  }
  clearKeypoints()
}


const updateSlamInfo = (data) => {
  slamInfo.value.keyFrames = data.keyFrames || slamInfo.value.keyFrames
  slamInfo.value.mapPoints = data.mapPoints || slamInfo.value.mapPoints
  slamInfo.value.trajectoryLength = data.trajectoryLength || slamInfo.value.trajectoryLength
  slamInfo.value.progress = data.progress || slamInfo.value.progress
}

const handleMainVideoLoaded = () => {
  if (mainVideo.value) {
    mainVideoInfo.value.width = mainVideo.value.videoWidth
    mainVideoInfo.value.height = mainVideo.value.videoHeight
    mainVideoInfo.value.fps = 30 // 默认帧率
    mainVideoPlaying.value = true
    
    // 初始化关键点Canvas
    setTimeout(() => {
      if (keypointCanvas.value) {
        const canvas = keypointCanvas.value
        const ctx = canvas.getContext('2d')
        canvas.width = mainVideo.value.videoWidth || mainVideo.value.clientWidth
        canvas.height = mainVideo.value.videoHeight || mainVideo.value.clientHeight
        console.log('🎯 关键点Canvas已初始化:', canvas.width, 'x', canvas.height)
      }
    }, 500)
  }
}

const handleSlamVideoLoaded = () => {
  slamVideoPlaying.value = true
  
  // 初始化关键点Canvas
  setTimeout(() => {
    if (keypointCanvas.value) {
      const canvas = keypointCanvas.value
      const ctx = canvas.getContext('2d')
      canvas.width = slamVideo.value.clientWidth || 320
      canvas.height = slamVideo.value.clientHeight || 240
      console.log('🎯 小视频窗口关键点Canvas已初始化:', canvas.width, 'x', canvas.height)
    }
  }, 500)
}

const handleMainVideoError = (error) => {
  console.error('主视频流错误:', error)
  mainVideoStatus.value = 'ERROR'
}

const handleSlamVideoError = (error) => {
  console.error('SLAM视频流错误:', error)
  slamVideoStatus.value = 'ERROR'
}

const reconnect = () => {
  disconnectWebRTC()
  setTimeout(() => {
    connectWebRTC()
  }, 1000)
}

// 帧捕获和处理
const startFrameCapture = () => {
  if (frameCaptureInterval.value) {
    clearInterval(frameCaptureInterval.value)
  }
  
  // 确保视频元素存在且正在播放
  if (!mainVideo.value) {
    console.warn('视频元素不存在，无法开始帧捕获')
    return
  }
  
  if (mainVideo.value.ended) {
    console.warn('视频已结束，无法开始帧捕获')
    return
  }
  
  // 如果视频暂停，尝试播放
  if (mainVideo.value.paused) {
    try {
      mainVideo.value.play()
      console.log('视频已重新开始播放')
    } catch (error) {
      console.warn('无法播放视频，无法开始帧捕获:', error)
      return
    }
  }
  
  frameProcessingEnabled.value = true
  frameCaptureInterval.value = setInterval(() => {
    captureAndProcessFrame()
  }, 300) // 每300ms捕获一帧，减少处理频率
  
  console.log('开始帧捕获和处理')
}


const stopFrameCapture = () => {
  if (frameCaptureInterval.value) {
    clearInterval(frameCaptureInterval.value)
    frameCaptureInterval.value = null
  }
  frameProcessingEnabled.value = false
}

const captureAndProcessFrame = async () => {
  if (!mainVideo.value || !frameProcessingEnabled.value) {
    return
  }
  
  try {
    // 检查视频元素是否有效
    if (!mainVideo.value.videoWidth || !mainVideo.value.videoHeight) {
      console.warn('视频尺寸无效，跳过帧捕获')
      return
    }
    
  // 检查视频是否正在播放 - 更宽松的检查
  if (mainVideo.value.ended) {
    console.warn('视频已结束，跳过帧捕获')
    return
  }
  
  // 如果视频暂停，尝试播放
  if (mainVideo.value.paused) {
    try {
      await mainVideo.value.play()
      console.log('视频已重新开始播放')
    } catch (error) {
      console.warn('无法播放视频，跳过帧捕获:', error)
      return
    }
  }
    
    // 创建canvas来捕获帧
    const canvas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')
    
    if (!ctx) {
      console.error('无法获取canvas上下文')
      return
    }
    
    canvas.width = mainVideo.value.videoWidth
    canvas.height = mainVideo.value.videoHeight
    
    // 绘制当前帧到canvas
    ctx.drawImage(mainVideo.value, 0, 0, canvas.width, canvas.height)
    
    // 转换为base64（降低质量减少数据大小）
    const frameData = canvas.toDataURL('image/jpeg', 0.5).split(',')[1]
    
    if (!frameData || frameData === 'data:,') {
      console.warn('帧数据无效，跳过处理')
      return
    }
    
    console.log('📸 捕获帧数据:', {
      canvasSize: `${canvas.width}x${canvas.height}`,
      dataLength: frameData.length,
      dataStart: frameData.substring(0, 50)
    })
    
    // 发送到后端处理
    console.log('🚀 准备调用sendFrameToProcessor')
    await sendFrameToProcessor(frameData)
    console.log('✅ sendFrameToProcessor调用完成')
    
  } catch (error) {
    console.error('帧捕获失败:', error)
  }
}

const sendFrameToProcessor = async (frameData, retryCount = 0) => {
  const maxRetries = 3
  const retryDelay = 1000 * (retryCount + 1) // 递增延迟
  
  try {
    // 检查是否有选中的会话
    if (!selectedSessionId.value) {
      console.warn('⚠️ 没有选中会话，跳过帧发送')
      return
    }
    
    console.log('🚀 发送帧数据到后端会话:', {
      sessionId: selectedSessionId.value,
      dataLength: frameData.length,
      retryCount,
      timestamp: new Date().toISOString()
    })
    
    // 使用新的会话API发送帧
    const result = await slamSessionApi.addFrame(selectedSessionId.value, frameData)
    console.log('🔍 后端返回数据:', result)
    if (result.success && result.data.success) {
      console.log('✅ 帧发送成功')
      
      // 更新SLAM数据，包括关键点
      if (result.data.keypoints && Array.isArray(result.data.keypoints)) {
        slamInfo.value.keypoints = result.data.keypoints
        slamInfo.value.matches = result.data.matches || []
        console.log('🎯 更新关键点数据:', result.data.keypoints.length, '个关键点')
      }
      
      // 更新其他SLAM数据
      if (result.data.keyframes !== undefined) {
        slamInfo.value.keyframes = result.data.keyframes
      }
      if (result.data.map_points !== undefined) {
        slamInfo.value.mapPoints = result.data.map_points
      }
      if (result.data.trajectory_length !== undefined) {
        slamInfo.value.trajectoryLength = result.data.trajectory_length
      }
      
      // 调用updateSlamDataFromProcessor来更新3D可视化
      console.log('🚀 调用updateSlamDataFromProcessor更新3D可视化')
      updateSlamDataFromProcessor(result.data)
    } else {
      console.warn('⚠️ 帧发送失败:', result.data?.message || result.error)
      throw new Error(result.data?.message || result.error || '帧发送失败')
    }
  } catch (error) {
    if (error.name === 'AbortError') {
      console.warn(`⏰ 请求超时 (尝试 ${retryCount + 1}/${maxRetries})`)
    } else {
      console.warn(`❌ 发送帧失败 (尝试 ${retryCount + 1}/${maxRetries}):`, error.message)
    }
    
    // 重试逻辑
    if (retryCount < maxRetries) {
      console.log(`🔄 ${retryDelay}ms后重试...`)
      setTimeout(() => {
        sendFrameToProcessor(frameData, retryCount + 1)
      }, retryDelay)
    } else {
      console.error('❌ 达到最大重试次数，跳过此帧')
    }
  }
}

// 添加防抖机制
let slamUpdateTimeout = null

const updateSlamDataFromProcessor = (result) => {
  try {
    // 清除之前的更新
    if (slamUpdateTimeout) {
      clearTimeout(slamUpdateTimeout)
    }
    
    // 延迟更新，避免过于频繁
    // 增加延迟时间，确保数据稳定
    slamUpdateTimeout = setTimeout(() => {
      try {
        // 安全地更新SLAM信息
        slamInfo.value = {
          ...slamInfo.value,
          trajectoryLength: result.trajectory_length || 0,
          keyframes: result.keyframes || 0,
          mapPoints: result.map_points || 0,
          mapPoints3D: Array.isArray(result.map_points_3d) ? result.map_points_3d : [],
          trajectoryPoints: Array.isArray(result.trajectory_points) ? result.trajectory_points : [],
          cameraPoses: Array.isArray(result.camera_poses) ? result.camera_poses : [],
          progress: Math.min(100, (result.frame_id || 0) * 0.1),
          has3DCoordinates: true,
          algorithm: 'Frontend Stream SLAM',
          keypoints: Array.isArray(result.keypoints) ? result.keypoints : [],
          matches: Array.isArray(result.matches) ? result.matches : [],
          processedFrame: result.processed_frame || null  // 添加处理后图片
        }
        
        // 启用3D可视化更新
        console.log('ℹ️ 启用3D可视化更新，同步轨迹数据')
        
        // 显示SLAM处理统计信息
        console.log('📊 SLAM处理统计:', {
          frameId: result.frame_id || 0,
          keyframes: result.keyframes || 0,
          mapPoints: result.map_points || 0,
          trajectoryLength: result.trajectory_length || 0,
          trajectoryPoints: Array.isArray(result.trajectory_points) ? result.trajectory_points : [],
          mapPoints3D: result.map_points_3d?.length || 0,
          cameraPoses: result.camera_poses?.length || 0,
          keypoints: Array.isArray(result.keypoints) ? result.keypoints : [],
          matches: Array.isArray(result.matches) ? result.matches : []
        })
        
        console.log('🎯 关键点数据:', result.keypoints?.slice(0, 5)) // 显示前5个关键点
        console.log('🔗 匹配数据:', result.matches?.slice(0, 3)) // 显示前3个匹配
        
        // 绘制关键点和匹配
        console.log('🔍 准备绘制关键点，数据检查:', {
          hasKeypointCanvas: !!keypointCanvas.value,
          hasSlamVideo: !!slamVideo.value,
          keypointsData: slamInfo.value.keypoints?.length || 0,
          matchesData: slamInfo.value.matches?.length || 0
        })
        
        // 测试绘制功能
        testKeypointDrawing()
        
        // 检查是否有处理后图片
        console.log('🖼️ 检查处理后图片:', {
          hasProcessedFrame: !!slamInfo.value.processedFrame,
          processedFrameLength: slamInfo.value.processedFrame?.length || 0,
          keypointsCount: slamInfo.value.keypoints?.length || 0,
          matchesCount: slamInfo.value.matches?.length || 0
        })
        
        // 如果有处理后图片，直接显示
        if (slamInfo.value.processedFrame) {
          console.log('✅ 使用后端处理后的图片')
          displayProcessedFrame()
        } else {
          console.log('⚠️ 没有处理后图片，使用前端绘制')
          // 否则调用绘制函数
          drawKeypointsAndMatches()
        }
        
        // 安全地更新3D可视化
        try {
          if (slamVisualization3D.value && 
              typeof slamVisualization3D.value === 'object' && 
              typeof slamVisualization3D.value.updateSlamData === 'function') {
            
            const slamData = {
              trajectoryPoints: Array.isArray(result.trajectory_points) ? result.trajectory_points : [],
              mapPoints3D: Array.isArray(result.map_points_3d) ? result.map_points_3d : [],
              cameraPoses: Array.isArray(result.camera_poses) ? result.camera_poses : [],
              keyframes: result.keyframes || 0,
              mapPoints: result.map_points || 0,
              trajectoryLength: result.trajectory_length || 0,
              progress: result.progress || 0
            }
            
            console.log('🎯 准备更新3D可视化数据:', {
              trajectoryPoints: slamData.trajectoryPoints.length,
              mapPoints3D: slamData.mapPoints3D.length,
              cameraPoses: slamData.cameraPoses.length,
              keyframes: slamData.keyframes,
              mapPoints: slamData.mapPoints,
              trajectoryLength: slamData.trajectoryLength,
              progress: slamData.progress
            })
            
            // 添加轨迹点调试信息
            if (slamData.trajectoryPoints.length > 0) {
              console.log('🔍 轨迹点数据示例:', slamData.trajectoryPoints[0])
              console.log('🔍 轨迹点总数:', slamData.trajectoryPoints.length)
              console.log('🔍 轨迹长度:', slamData.trajectoryLength)
            } else {
              console.log('❌ 没有轨迹点数据')
            }
            
            // 添加原始数据调试
            console.log('🔍 原始result.trajectory_length:', result.trajectory_length)
            console.log('🔍 原始result.trajectory_points长度:', result.trajectory_points?.length || 0)
            
        // 添加详细日志来诊断问题
        console.log('🔍 原始SLAM数据:', {
          trajectory_points: result.trajectory_points,
          map_points_3d: result.map_points_3d,
          camera_poses: result.camera_poses,
          frame_id: result.frame_id,
          keypoints: Array.isArray(result.keypoints) ? result.keypoints : [],
          matches: Array.isArray(result.matches) ? result.matches : [],
          hasProcessedFrame: !!result.processed_frame
        })

        // 确保数据格式正确 - 更严格的验证
        if (Array.isArray(slamData.trajectoryPoints) && slamData.trajectoryPoints.length > 0) {
          slamData.trajectoryPoints = slamData.trajectoryPoints.filter(point => point != null).map(point => {
            if (point && typeof point === 'object' && point.x !== undefined && point.y !== undefined && point.z !== undefined) {
              return {
                x: parseFloat(point.x) || 0,
                y: parseFloat(point.y) || 0,
                z: parseFloat(point.z) || 0,
                frame_id: parseInt(point.frame_id) || 0
              }
            }
            return { x: 0, y: 0, z: 0, frame_id: 0 }
          })
        } else {
          slamData.trajectoryPoints = []
        }
        
        if (Array.isArray(slamData.mapPoints3D) && slamData.mapPoints3D.length > 0) {
          slamData.mapPoints3D = slamData.mapPoints3D.filter(point => point != null).map(point => {
            if (point && typeof point === 'object' && point.x !== undefined && point.y !== undefined && point.z !== undefined) {
              return {
                x: parseFloat(point.x) || 0,
                y: parseFloat(point.y) || 0,
                z: parseFloat(point.z) || 0,
                frame_id: parseInt(point.frame_id) || 0
              }
            }
            return { x: 0, y: 0, z: 0, frame_id: 0 }
          })
        } else {
          slamData.mapPoints3D = []
        }
        
        // 确保cameraPoses也是数组，并处理矩阵格式
        if (!Array.isArray(slamData.cameraPoses)) {
          slamData.cameraPoses = []
        } else {
          // 处理cameraPoses数据格式
          slamData.cameraPoses = slamData.cameraPoses.map(pose => {
            if (Array.isArray(pose) && pose.length === 4 && Array.isArray(pose[0])) {
              // 4x4矩阵格式，转换为对象格式
              return {
                x: parseFloat(pose[0][3]) || 0,  // 平移向量的x
                y: parseFloat(pose[1][3]) || 0,  // 平移向量的y
                z: parseFloat(pose[2][3]) || 0,  // 平移向量的z
                qx: 0, qy: 0, qz: 0, qw: 1,     // 默认四元数
                frame_id: 0
              }
            } else if (pose && typeof pose === 'object' && pose.x !== undefined) {
              // 已经是对象格式
              return {
                x: parseFloat(pose.x) || 0,
                y: parseFloat(pose.y) || 0,
                z: parseFloat(pose.z) || 0,
                qx: parseFloat(pose.qx) || 0,
                qy: parseFloat(pose.qy) || 0,
                qz: parseFloat(pose.qz) || 0,
                qw: parseFloat(pose.qw) || 1,
                frame_id: parseInt(pose.frame_id) || 0
              }
            } else {
              // 默认值
              return { x: 0, y: 0, z: 0, qx: 0, qy: 0, qz: 0, qw: 1, frame_id: 0 }
            }
          })
        }

        // 添加处理后的数据日志
        console.log('🔍 处理后的SLAM数据:', {
          trajectoryPointsLength: slamData.trajectoryPoints.length,
          mapPoints3DLength: slamData.mapPoints3D.length,
          cameraPosesLength: slamData.cameraPoses.length,
          firstTrajectoryPoint: slamData.trajectoryPoints[0],
          firstMapPoint3D: slamData.mapPoints3D[0],
          firstCameraPose: slamData.cameraPoses[0]
        })
            
            // 使用异步方式更新3D可视化，避免阻塞主线程
            const update3DVisualization = () => {
              try {
            // 最后的安全检查
            console.log('🔍 3D可视化组件状态检查:', {
              componentExists: !!slamVisualization3D.value,
              componentType: typeof slamVisualization3D.value,
              hasUpdateMethod: slamVisualization3D.value && typeof slamVisualization3D.value.updateSlamData === 'function',
              slamDataValid: !!(slamData && Array.isArray(slamData.trajectoryPoints) && Array.isArray(slamData.mapPoints3D) && Array.isArray(slamData.cameraPoses))
            })
            
            if (slamVisualization3D.value && 
                typeof slamVisualization3D.value.updateSlamData === 'function' &&
                slamData &&
                Array.isArray(slamData.trajectoryPoints) &&
                Array.isArray(slamData.mapPoints3D) &&
                Array.isArray(slamData.cameraPoses)) {
              
              console.log('🚀 开始异步更新3D可视化...')
              try {
                // 先测试3D组件是否响应
                console.log('🔍 测试3D组件响应性...')
                console.log('🚀 调用3D可视化更新，数据:', slamData)
            slamVisualization3D.value.updateSlamData(slamData)
                console.log('✅ 3D可视化数据更新成功')
                
                // 验证数据是否真的被更新
                setTimeout(() => {
                  console.log('🔍 验证3D组件数据更新结果...')
                  if (slamVisualization3D.value && typeof slamVisualization3D.value.getSlamData === 'function') {
                    const currentData = slamVisualization3D.value.getSlamData()
                    console.log('📊 3D组件当前数据:', currentData)
                  }
                }, 100)
              } catch (updateError) {
                console.error('❌ 3D可视化更新过程中出错:', updateError)
                console.error('❌ 错误堆栈:', updateError.stack)
              }
            } else {
              console.warn('⚠️ 3D可视化组件不可用或数据格式错误，跳过更新')
              console.warn('⚠️ 组件状态:', {
                exists: !!slamVisualization3D.value,
                hasMethod: slamVisualization3D.value && typeof slamVisualization3D.value.updateSlamData === 'function',
                dataValid: !!(slamData && Array.isArray(slamData.trajectoryPoints) && Array.isArray(slamData.mapPoints3D) && Array.isArray(slamData.cameraPoses))
              })
            }
              } catch (updateError) {
                console.error('❌ 3D可视化数据更新失败:', updateError)
                // 如果更新失败，尝试重置3D可视化组件
                try {
                  if (slamVisualization3D.value && typeof slamVisualization3D.value.reset === 'function') {
                    slamVisualization3D.value.reset()
                    console.log('🔄 3D可视化组件已重置')
                  }
                } catch (resetError) {
                  console.error('❌ 重置3D可视化组件失败:', resetError)
                }
              }
            }

            // 使用多重异步机制确保稳定更新
            const safeUpdate3D = async () => {
              try {
                // 等待一小段时间确保DOM更新完成
                await new Promise(resolve => setTimeout(resolve, 50))
                
                // 使用 requestAnimationFrame 确保在下一个渲染周期执行
                await new Promise(resolve => requestAnimationFrame(resolve))
                
                // 最终数据验证
                const finalSlamData = {
                  trajectoryPoints: slamData.trajectoryPoints || [],
                  mapPoints3D: slamData.mapPoints3D || [],
                  cameraPoses: slamData.cameraPoses || []
                }
                
                console.log('🔍 最终3D可视化数据验证:', {
                  trajectoryPointsValid: Array.isArray(finalSlamData.trajectoryPoints),
                  mapPoints3DValid: Array.isArray(finalSlamData.mapPoints3D),
                  cameraPosesValid: Array.isArray(finalSlamData.cameraPoses),
                  trajectoryPointsLength: finalSlamData.trajectoryPoints.length,
                  mapPoints3DLength: finalSlamData.mapPoints3D.length,
                  cameraPosesLength: finalSlamData.cameraPoses.length
                })
                
                // 执行更新
                update3DVisualization()
              } catch (error) {
                console.error('❌ 异步3D可视化更新失败:', error)
              }
            }

            // 启动安全的异步更新，添加错误边界
            try {
              safeUpdate3D()
            } catch (error) {
              console.error('❌ 启动3D可视化更新失败:', error)
              // 即使3D可视化失败，也不影响SLAM数据处理
              console.log('ℹ️ 继续SLAM数据处理，跳过3D可视化更新')
            }
          }
        } catch (error) {
          console.error('3D可视化更新失败:', error)
        }
      } catch (error) {
        console.error('更新SLAM数据失败:', error)
      }
    }, 300) // 300ms延迟，确保数据稳定和避免过于频繁的更新
  } catch (error) {
    console.error('更新SLAM数据失败:', error)
  }
}

// 连接状态监控
let connectionMonitor = null

const startConnectionMonitoring = () => {
  if (connectionMonitor) {
    clearInterval(connectionMonitor)
  }
  
  connectionMonitor = setInterval(() => {
    if (peerConnection) {
      const state = peerConnection.connectionState
      const iceState = peerConnection.iceConnectionState
      
      // 减少日志输出，避免干扰
      // console.log('连接状态监控:', { state, iceState })
      
      // 更严格的连接状态检查，避免误判
      if (state === 'failed' || (state === 'disconnected' && iceState === 'failed')) {
        console.warn('⚠️ WebRTC连接状态异常:', state, iceState)
        connectionStatus.value = 'ERROR'
        connected.value = false
        
        // 停止监控并尝试重连
        clearInterval(connectionMonitor)
        connectionMonitor = null
        
        setTimeout(() => {
          console.log('🔄 连接状态异常，尝试重连...')
          reconnect()
        }, 3000) // 增加重连延迟
      } else if (state === 'connected' && iceState === 'connected' && !connected.value) {
        console.log('✅ WebRTC连接已恢复')
        connectionStatus.value = 'CONNECTED'
        connected.value = true
      }
    }
  }, 10000) // 减少监控频率到10秒
}

const stopConnectionMonitoring = () => {
  if (connectionMonitor) {
    clearInterval(connectionMonitor)
    connectionMonitor = null
  }
}

// 简化的视频状态检查
let simpleVideoCheck = null

const startSimpleVideoCheck = () => {
  if (simpleVideoCheck) {
    clearInterval(simpleVideoCheck)
  }
  
  simpleVideoCheck = setInterval(() => {
    if (mainVideo.value && connected.value) {
      // 更严格的视频状态检查，避免误判
      const isVideoPlaying = !mainVideo.value.paused && mainVideo.value.readyState >= 2
      const hasVideoContent = mainVideo.value.videoWidth > 0 && mainVideo.value.videoHeight > 0
      
      // 只有在真正暂停且应该播放时才恢复
      if (mainVideo.value.paused && mainVideoPlaying.value && mainVideo.value.readyState >= 2) {
        console.warn('⚠️ 检测到视频暂停，尝试恢复播放')
        forcePlayVideo()
      } 
      // 只有在有流但确实没有内容时才处理黑屏
      else if (mainVideo.value.srcObject && !hasVideoContent && mainVideo.value.readyState >= 2) {
        console.warn('⚠️ 检测到可能的黑屏，视频有流但无尺寸')
        // 延迟处理，避免频繁操作
        setTimeout(() => {
          if (mainVideo.value && mainVideo.value.srcObject && mainVideo.value.videoWidth === 0) {
            const stream = mainVideo.value.srcObject
            mainVideo.value.srcObject = null
            setTimeout(() => {
              if (mainVideo.value) {
                mainVideo.value.srcObject = stream
                mainVideo.value.play().catch(err => console.error('重新设置流播放失败:', err))
              }
            }, 200)
          }
        }, 1000)
      }
      // 正常情况下的日志（减少输出）
      else if (hasVideoContent && isVideoPlaying) {
        // console.log('✅ 视频正常播放，尺寸:', mainVideo.value.videoWidth, 'x', mainVideo.value.videoHeight)
      }
    }
  }, 15000) // 减少检查频率到15秒
}

const stopSimpleVideoCheck = () => {
  if (simpleVideoCheck) {
    clearInterval(simpleVideoCheck)
    simpleVideoCheck = null
  }
}

// 视频流监控
let videoStreamMonitor = null

const startVideoStreamMonitoring = () => {
  if (videoStreamMonitor) {
    clearInterval(videoStreamMonitor)
  }
  
  videoStreamMonitor = setInterval(() => {
    if (mainVideo.value && mainVideo.value.srcObject) {
      const stream = mainVideo.value.srcObject
      const tracks = stream.getTracks()
      
      console.log('视频流监控:', {
        hasStream: !!stream,
        trackCount: tracks.length,
        videoTrack: tracks.find(t => t.kind === 'video')?.readyState,
        audioTrack: tracks.find(t => t.kind === 'audio')?.readyState,
        videoPaused: mainVideo.value.paused,
        videoCurrentTime: mainVideo.value.currentTime
      })
      
      // 检查视频轨道状态
      const videoTrack = tracks.find(t => t.kind === 'video')
      if (videoTrack && videoTrack.readyState === 'ended') {
        console.warn('⚠️ 视频轨道已结束，尝试重新播放')
        mainVideo.value.play().catch(err => {
          console.error('重新播放失败:', err)
        })
      }
      
      // 如果视频被暂停但应该播放，则恢复播放
      if (mainVideo.value.paused && mainVideoPlaying.value) {
        console.warn('⚠️ 视频被暂停但状态显示应该播放，尝试恢复播放')
        mainVideo.value.play().catch(err => {
          console.error('恢复播放失败:', err)
        })
      }
      
      // 检查视频是否卡住
      if (!mainVideo.value.paused && mainVideo.value.currentTime > 0) {
        const lastTime = mainVideo.value.currentTime
        setTimeout(() => {
          if (Math.abs(mainVideo.value.currentTime - lastTime) < 0.1) {
            console.warn('⚠️ 视频可能卡住，尝试重新播放')
            mainVideo.value.play().catch(err => {
              console.error('重新播放失败:', err)
            })
          }
        }, 3000)
      }
    }
  }, 5000) // 每5秒检查一次
}

const stopVideoStreamMonitoring = () => {
  if (videoStreamMonitor) {
    clearInterval(videoStreamMonitor)
    videoStreamMonitor = null
  }
}

const toggleFullscreen = () => {
  if (!fullscreen.value) {
    enterFullscreen()
  } else {
    exitFullscreen()
  }
}

const enterFullscreen = () => {
  fullscreen.value = true
  document.body.style.overflow = 'hidden'
}

const exitFullscreen = () => {
  fullscreen.value = false
  document.body.style.overflow = 'auto'
}

const testVideoSource = async () => {
  try {
    console.log('测试视频源:', webrtcConfig.videoSource)
    
    // 创建测试视频元素
    const testVideo = document.createElement('video')
    testVideo.src = webrtcConfig.videoSource
    testVideo.autoplay = true
    testVideo.muted = true
    testVideo.playsInline = true
    testVideo.crossOrigin = 'anonymous'
    
    // 测试视频加载
    await new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('视频源连接超时'))
      }, 10000)
      
      testVideo.onloadedmetadata = () => {
        clearTimeout(timeout)
        console.log('视频源测试成功')
        alert('视频源连接成功！')
        resolve()
      }
      
      testVideo.oncanplay = () => {
        clearTimeout(timeout)
        console.log('视频源可以播放')
        alert('视频源连接成功！')
        resolve()
      }
      
      testVideo.onerror = (error) => {
        clearTimeout(timeout)
        console.error('视频源测试失败:', error)
        alert('视频源连接失败: ' + error.message)
        reject(error)
      }
    })
    
  } catch (error) {
    console.error('测试视频源失败:', error)
    alert('测试视频源失败: ' + error.message)
  }
}

// SLAM控制功能
const startSlam = async () => {
  try {
    console.log('🚀 启动SLAM处理...')
    
    // 检查会话选择
    if (!selectedSessionId.value) {
      console.warn('未选择会话，无法启动SLAM')
      alert('请先选择会话！')
      return
    }
    
    // 检查WebRTC连接状态
    if (!connected.value) {
      console.warn('WebRTC未连接，无法启动SLAM')
      alert('请先连接视频流！')
      return
    }
    
    // 强制停止任何现有的SLAM会话
    if (slamProcessing.value) {
      console.log('🔄 停止现有SLAM会话，准备启动新会话')
      await stopSlam()
      // 等待停止完成
      await new Promise(resolve => setTimeout(resolve, 500))
    }
    
    // 清理3D可视化数据
    if (slamVisualization3D.value && typeof slamVisualization3D.value.reset3DVisualization === 'function') {
      slamVisualization3D.value.reset3DVisualization()
      console.log('🔄 已清理3D可视化数据')
    }
    
    // 启动SLAM会话
    const result = await slamSessionApi.startSlam(selectedSessionId.value)
    if (result.success && result.data.success) {
      console.log('✅ SLAM会话启动成功')
      slamProcessing.value = true
      slamStatus.value = 'RUNNING'
      
      // 开始SLAM信息更新
      startSlamInfoUpdate()
      
      // 更新3D可视化状态
      if (slamVisualization3D.value && typeof slamVisualization3D.value.updateSlamStatus === 'function') {
        slamVisualization3D.value.updateSlamStatus('running')
        console.log('✅ 3D可视化状态已更新为运行中')
      }
      
      // 开始帧捕获
      startFrameCapture()
      
      // 开始发送视频帧到后端
      startVideoFrameSending()
      
      // 开始关键点绘制
      startKeypointDrawing()
      
      alert('SLAM处理启动成功！')
    } else {
      console.error('❌ SLAM会话启动失败:', result.data?.message || result.error)
      alert('SLAM启动失败: ' + (result.data?.message || result.error))
    }
    
    // 清理SLAM数据
    slamInfo.value = {
      keyframes: 0,
      mapPoints: 0,
      trajectoryLength: 0.0,
      progress: 0.0,
      trajectoryPoints: [],
      mapPoints3D: [],
      cameraPoses: [],
      keypoints: [],
      matches: [],
      processedFrame: null
    }
    
    // 检查视频状态
    console.log('启动前视频状态:', {
      mainVideoPlaying: mainVideoPlaying.value,
      mainVideoStatus: mainVideoStatus.value,
      mainVideoSrcObject: mainVideo.value?.srcObject,
      connected: connected.value
    })
    
    // 旧的SLAM进程启动代码已删除，现在使用新的会话API
    
    // 设置SLAM视频源
    if (mainVideo.value && mainVideo.value.srcObject && slamVideo.value) {
      slamVideo.value.srcObject = mainVideo.value.srcObject
      console.log('✅ SLAM视频源已设置')
      
      // 确保SLAM视频开始播放
      slamVideo.value.play().then(() => {
        slamVideoPlaying.value = true
        slamVideoStatus.value = 'PLAYING'
        console.log('✅ SLAM视频开始播放')
      }).catch(err => {
        console.error('❌ SLAM视频播放失败:', err)
        slamVideoStatus.value = 'ERROR'
      })
    }
    
    // 注意：SLAM数据更新已在上面启动，这里不需要重复调用
    // startSlamInfoUpdate()
    
    // 开始帧捕获
    startFrameCapture()
    
    // 开始发送视频帧到后端
    startVideoFrameSending()
    
    // 开始关键点绘制
    startKeypointDrawing()
    
    // 检查启动后视频状态
    setTimeout(() => {
      console.log('启动后视频状态:', {
        mainVideoPlaying: mainVideoPlaying.value,
        mainVideoStatus: mainVideoStatus.value,
        mainVideoSrcObject: mainVideo.value?.srcObject,
        slamVideoSrcObject: slamVideo.value?.srcObject,
        connected: connected.value
      })
    }, 1000)
    
    // 注释掉外部API调用
    /*
    const response = await fetch('http://8.136.52.147:8081/api/slam/start', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        sessionId: 'slam-session-' + Date.now(),
        mode: 'webrtc'
      })
    })
    
    if (response.ok) {
      const result = await response.json()
      console.log('SLAM启动成功:', result)
      slamProcessing.value = true
      console.log('✅ SLAM处理已启动，WebRTC连接保持稳定')
    } else {
      throw new Error('SLAM启动失败: ' + response.status)
    }
    */
  } catch (error) {
    console.error('启动SLAM失败:', error)
    slamProcessing.value = false
    alert('启动SLAM失败: ' + error.message)
  }
}

const stopSlam = async () => {
  try {
    console.log('🛑 停止SLAM进程...')
    
    // 检查是否有选中的会话
    if (!selectedSessionId.value) {
      console.warn('没有选中的会话，无法停止SLAM')
      return
    }
    
    // 停止SLAM会话
    const result = await slamSessionApi.stopSlam(selectedSessionId.value)
    if (result.success && result.data.success) {
      console.log('✅ SLAM会话已停止')
      slamProcessing.value = false
      slamStatus.value = 'STOPPED'
      
      // 清空SLAM数据
      slamInfo.value = {
        keypoints: [],
        matches: [],
        keyframes: 0,
        mapPoints: 0,
        trajectoryLength: 0,
        trajectoryPoints: [],
        mapPoints3D: [],
        cameraPoses: []
      }
      console.log('✅ SLAM数据已清空')
      
      // 更新3D可视化状态
      if (slamVisualization3D.value && typeof slamVisualization3D.value.updateSlamStatus === 'function') {
        slamVisualization3D.value.updateSlamStatus('stopped')
        console.log('✅ 3D可视化状态已更新为已停止')
      }
      
      // 清空3D可视化数据
      if (slamVisualization3D.value && typeof slamVisualization3D.value.reset3DVisualization === 'function') {
        slamVisualization3D.value.reset3DVisualization()
        console.log('✅ 3D可视化数据已清空')
      }
      
      // 停止所有相关进程
      if (updateInterval) {
        clearInterval(updateInterval)
        updateInterval = null
      }
      
      // 停止帧捕获
      if (frameCaptureInterval.value) {
        clearInterval(frameCaptureInterval.value)
        frameCaptureInterval.value = null
        frameProcessingEnabled.value = false
      }
      
      alert('SLAM处理已停止！')
    } else {
      console.error('❌ 停止SLAM会话失败:', result.data?.message || result.error)
      alert('停止SLAM失败: ' + (result.data?.message || result.error))
    }
  } catch (error) {
    console.error('❌ 停止SLAM进程失败:', error)
    alert('停止SLAM失败: ' + error.message)
  }
}

// 加入全局SLAM
// 移除全局SLAM相关函数


const pauseSlam = async () => {
  try {
    console.log('暂停SLAM处理...')
    
    const response = await fetch('http://8.136.52.147:8081/api/slam/pause', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      }
    })
    
    if (response.ok) {
      const result = await response.json()
      console.log('SLAM暂停成功:', result)
      alert('SLAM处理已暂停！')
    } else {
      throw new Error('SLAM暂停失败: ' + response.status)
    }
  } catch (error) {
    console.error('暂停SLAM失败:', error)
    alert('暂停SLAM失败: ' + error.message)
  }
}

// 会话管理函数
const loadAvailableSessions = async () => {
  try {
    const result = await slamSessionApi.getSessions()
    console.log('🔍 API响应:', result)
    
    // 处理Spring Boot后端格式 {success: true, data: {code: 200, data: {sessions: [...]}}}
    if (result.success && result.data.code === 200) {
      // 转换数据格式以适配现有界面
      availableSessions.value = result.data.data.sessions.map(session => ({
        id: session.id,
        sessionName: session.sessionName,
        status: session.status,
        framesReceived: session.currentFrame || 0,
        framesProcessed: session.currentFrame || 0,
        slamStarted: session.status === 'RUNNING',
        createdAt: session.createdAt,
        lastActivity: session.startedAt || session.createdAt,
        mapId: session.mapId,
        videoSource: session.videoSource || 'webrtc://8.136.52.147/live/livestream'
      }))
      console.log('✅ 加载可用会话成功 (Spring Boot格式):', availableSessions.value)
      
      // 自动选择第一个会话作为默认会话
      if (availableSessions.value.length > 0 && !selectedSessionId.value) {
        selectedSessionId.value = availableSessions.value[0].id
        console.log('🎯 自动选择默认会话:', selectedSessionId.value)
      }
    }
    // 处理Python SLAM服务格式 {success: true, data: {data: {success: true, sessions: [...]}}}
    else if (result.success && result.data.data?.success) {
      // 转换数据格式以适配现有界面
      const content = result.data.data?.sessions || []
      availableSessions.value = content.map(session => ({
        id: session.id,
        sessionName: session.sessionName,
        status: session.status,
        framesReceived: 0,
        framesProcessed: 0,
        slamStarted: session.status === 'RUNNING',
        createdAt: new Date().toISOString(),
        lastActivity: new Date().toISOString(),
        mapId: null,
        videoSource: 'webrtc://8.136.52.147/live/livestream'
      }))
      console.log('✅ 加载可用会话成功 (Python SLAM格式):', availableSessions.value)
      
      // 自动选择第一个会话作为默认会话
      if (availableSessions.value.length > 0 && !selectedSessionId.value) {
        selectedSessionId.value = availableSessions.value[0].id
        console.log('🎯 自动选择默认会话:', selectedSessionId.value)
      }
    } else {
      console.error('❌ 加载会话失败:', result.data?.message || result.error)
    }
  } catch (error) {
    console.error('❌ 加载会话列表失败:', error)
  }
}

const createNewSession = async () => {
  try {
    const sessionName = prompt('请输入会话名称（可选）:')
    const result = await slamSessionApi.createSession(sessionName || null)
    
    if (result.success && result.data.success) {
      const newSessionId = result.data.session_id
      console.log('✅ 创建新会话成功:', newSessionId)
      
      // 刷新会话列表
      await loadAvailableSessions()
      
      // 自动选择新创建的会话
      selectedSessionId.value = newSessionId
      
      alert(`会话创建成功！会话ID: ${newSessionId}`)
    } else {
      console.error('❌ 创建会话失败:', result.data?.message || result.error)
      alert('创建会话失败: ' + (result.data?.message || result.error))
    }
  } catch (error) {
    console.error('❌ 创建会话失败:', error)
    alert('创建会话失败: ' + error.message)
  }
}

const onSessionChange = () => {
  console.log('会话选择变更:', selectedSessionId.value)
  
  // 如果当前有SLAM在运行，先停止
  if (slamProcessing.value) {
    console.log('🔄 检测到SLAM正在运行，先停止当前会话')
    stopSlamProcessing()
  }
  
  // 清理3D可视化数据
  if (slamVisualization3D.value && typeof slamVisualization3D.value.reset3DVisualization === 'function') {
    slamVisualization3D.value.reset3DVisualization()
    console.log('🔄 已清理3D可视化数据')
  }
  
  // 清理SLAM数据
  slamInfo.value = {
    keyframes: 0,
    mapPoints: 0,
    trajectoryLength: 0.0,
    progress: 0.0,
    trajectoryPoints: [],
    mapPoints3D: [],
    cameraPoses: [],
    keypoints: [],
    matches: [],
    processedFrame: null
  }
  
  // 清空SLAM状态
  
  if (selectedSessionId.value) {
    console.log('✅ 已选择会话，可以启动SLAM')
    // 不自动启动，等待用户手动点击开始SLAM按钮
  } else {
    console.log('❌ 未选择会话，无法启动SLAM')
  }
}

// 生命周期
onMounted(() => {
  console.log('组件挂载，等待SLAM会话启动...')
  
  // 加载可用会话
  loadAvailableSessions()
  
  // 不自动启动SLAM数据更新，等待用户手动启动SLAM
  // startSlamInfoUpdate()
  
  // 尝试连接真实视频流，等待DOM完全渲染
  setTimeout(() => {
    connectionStatus.value = 'CONNECTING'
    connecting.value = true
    
    try {
      console.log('初始化WebRTC流媒体组件...')
      
      // 确保视频元素存在后再连接
      if (mainVideo.value) {
        connectWebRTC()
      } else {
        console.warn('视频元素未准备好，延迟连接...')
        setTimeout(() => {
          if (mainVideo.value) {
            connectWebRTC()
          } else {
            console.error('视频元素仍未准备好，连接失败')
            connectionStatus.value = 'DISCONNECTED'
            connecting.value = false
          }
        }, 2000)
      }
      
    } catch (error) {
      console.error('初始化失败:', error)
      connectionStatus.value = 'ERROR'
      errorMessage.value = error.message
    } finally {
      connecting.value = false
    }
  }, 2000) // 增加延迟时间
})

onUnmounted(() => {
  disconnectWebRTC()
  stopConnectionMonitoring()
  stopVideoStreamMonitoring()
  stopSimpleVideoCheck()
  stopKeypointDrawing()
  stopFrameCapture()
  if (updateInterval) {
    clearInterval(updateInterval)
  }
})
</script>

<style scoped>
.webrtc-stream-view {
  width: 100%;
}

video {
  display: block;
}

/* 全屏样式 */
.fixed.inset-0 {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 9999;
}
</style>
